现在我们开发Vue项目几乎都是用Vue-CLI去创建项目,因为它提供了几乎我们所有需要的功能,不再需要我们去自己配置像webpack、eslint、sass/less、unit test/e2e test这些功能,大大提升我们的效率。于是我们不会去深究它的技术细节,只知道如何去使用就好了。这对于一个前端肯定是不够的,不说精通,也一定要对其原理有一定了解。所以今天我们不用Vue-CLI,从零去创建一个Vue项目。

创建项目

mkdir vue-webpack
cd vue-webpack
npm init -y

创建入口文件

创建src文件夹,创建main.js文件

alert('hello vue')

接着创建index.html文件

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Vue Webpack</title>
</head>
<body>
    <div id="app">
    </div>
</body>
</html>

项目目录

src/
    main.js
    index.html

webpack基本安装配置

npm install webpack webpack-cli -D

接着在根目录创建webpack.config.js

const path = require('path')

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
}
  • entry是webpack读取的入口文件,可以有多个,但一般单页应用只有一个
  • output是打包输出配置
  • path 打包的目录,这里我配置为dist文件夹
  • filename 打包的文件命名
  • clean 打包前是否清空文件夹

修改package.json,将scripts修改为如下

"scripts": {
    "watch": "webpack --watch",
    "build": "webpack"
},
  • –watch 会监听文件修改,自动打包

测试打包是否成功

npm run build

# 打包成功
asset main.e8760e604a32a49cb09a.js 1.18 KiB [emitted] [immutable] (name: main)
./src/main.js 1 bytes [built] [code generated]
webpack 5.51.2 compiled successfully in 59 ms

打包成功后会生成dist文件夹

dist/
    main.e8760e604a32a49cb09a.js

缺少html文件,我们安装一个webpack插件

npm install html-webpack-plugin -D

修改webpack.config.js

const path = require('path')
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  plugins: [
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
}
  • html-webpack-plugin 会自动将打包好的JS注入到index.html中

重新测试打包

npm run build

打包成功后会生成dist文件夹

dist/
    index.html
    main.e8760e604a32a49cb09a.js

在浏览器中打开index.html,会弹出·‘hello vue’

Vue安装配置

接下来,安装Vue相关的配置

安装Vue和Vue Router

npm install vue vue-router

安装打包.Vue文件的webpack配置

npm install vue-loader vue-template-compiler -D

这里需要注意vue的版本要和vue-template-compiler的版本一致,否则会出现错误

修改main.js如下

import Vue from 'vue'
import VueRouter from 'vue-router'
import App from './App.vue'

import Foo from './pages/Foo.vue'
const Bar = () => import(/* webpackChunkName: "group-bar" */ './pages/Bar.vue') // 异步加载文件

Vue.use(VueRouter)

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

const router = new VueRouter({
  routes
})

new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app');

创建src/App.vue文件

<template>
  <div>
    <h1>hello vue webpack</h1>
    <ul>
      <li>
        <router-link to="/foo">Go to Foo</router-link>
      </li>
      <li>
        <router-link to="/bar">Go to Bar</router-link>
      </li>
    </ul>
    <router-view></router-view>
  </div>
</template>

在src下创建pages文件夹,创建Bar.vue和Foo.vue文件

Bar.vue

<template>
  <div>bar</div>
</template>

Foo.vue

<template>
  <div>foo</div>
</template>

修改webpack.config.js

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
    ]
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
}
  • webpack.ProgressPlugin() 用户在打包时会有个进度条

然后打包

npm run build

可以看到dist目录

dist/
    index.html
    group-bar.3235a6c307cef33d5703.js
    main.406dcc2772a126d0094b.js

在浏览器中打开index.html,测试发现页面正常显示,路由切换没有问题

安装Element UI框架

npm install element-ui

这里选择按需引入Element UI的组件,不是全量引入,所以要安装下面的一些依赖

npm install @babel/core babel-loader babel-plugin-component css-loader

修改main.js,引入Button组件和一个Calendar日历组件

import Vue from 'vue'
import VueRouter from 'vue-router'
import { Button, Calendar } from 'element-ui';
import App from './App.vue'

import Foo from './pages/Foo.vue'
const Bar = () => import(/* webpackChunkName: "group-bar" */ './pages/Bar.vue') // 异步加载文件

Vue.use(VueRouter)
// el component
Vue.use(Button)
Vue.use(Calendar)

const routes = [
  { path: '/foo', component: Foo },
  { path: '/bar', component: Bar }
]

const router = new VueRouter({
  routes
})

new Vue({
  router,
  render: (h) => h(App),
}).$mount('#app');

在根目录创建.babelrc文件,用于配置Babel的按需引入组件

{
    "plugins": [
      [
        "component",
        {
          "libraryName": "element-ui",
          "styleLibraryName": "theme-chalk"
        }
      ]
    ]
}

修改webpack.config.js

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
    ]
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
}

修改App.vue文件

<template>
  <div>
    <h1>hello vue webpack</h1>
    <ul>
      <li>
        <router-link to="/foo">Go to Foo</router-link>
      </li>
      <li>
        <router-link to="/bar">Go to Bar</router-link>
      </li>
    </ul>
    <router-view></router-view>
    <el-calendar v-model="value"></el-calendar>
    <el-button>默认按钮</el-button>
    <el-button type="primary">主要按钮</el-button>
    <el-button type="success">成功按钮</el-button>
    <el-button type="info">信息按钮</el-button>
    <el-button type="warning">警告按钮</el-button>
    <el-button type="danger">危险按钮</el-button>
  </div>
</template>

<script>
export default {
  data() {
    return {
      value: new Date()
    }
  }
}
</script>

<style scoped>
  h1 {
    color: blue
  }
</style>

打包

npm run build

在浏览器中打开index.html,测试发现路由切换没有问题,element组件正常显示

Webpack其他配置

最后我们还要配一些其他的有用配置

  • webpack-dev-server 用于开发时自动刷新,和模块热替换,提升开发效率
  • source-map 代码发生报错时,可以直接找到哪个文件的第几行
  • optimization的runtimeChunk、splitChunks配置,来配置打包的代码分割,项目依赖、runtime、业务代码分离文件打包。这样如果项目依赖、runtime没有变化的话,每次打包都不会重新生成文件,只有业务代码会生成新的文件。

webpack-dev-server安装

npm install webpack-dev-server -D

最后的webpack.config.js配置

const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
const HtmlWebpackPlugin = require('html-webpack-plugin');
const webpack = require('webpack');

module.exports = {
  mode: 'development',
  devtool: 'inline-source-map',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: '[name].[contenthash].js',
    clean: true,
  },
  optimization: {
    runtimeChunk: 'single',
    splitChunks: {
      cacheGroups: {
        vendor: {
          test: /[\\/]node_modules[\\/]/,
          name: 'vendors',
          chunks: 'all',
        },
       },
     },
  },
  devServer: {
    static: './dist',
    hot: true,
  },
  module: {
    rules: [
      {
        test: /\.vue$/,
        loader: 'vue-loader'
      },
      {
        test: /\.js$/,
        loader: 'babel-loader'
      },
      {
        test: /\.css$/,
        use: [
          'vue-style-loader',
          'css-loader'
        ]
      },
      {
        test: /\.(png|svg|jpg|jpeg|gif)$/i,
        type: 'asset/resource',
      },
      {
        test: /\.(woff|woff2|eot|ttf|otf)$/i,
        type: 'asset/resource',
      },
    ]
  },
  plugins: [
    new webpack.ProgressPlugin(),
    new VueLoaderPlugin(),
    new HtmlWebpackPlugin({ template: './src/index.html' })
  ]
}

修改package.json,增加start命令,用于开发时使用webpack-dev-server的自动刷新功能

"scripts": {
  "watch": "webpack --watch",
  "start": "webpack serve --open",
  "build": "webpack"
},

使用webpack-dev-server开发

npm start

浏览器自动打开,修改代码,浏览器会自动刷新