之前配置了一次 Vue + babel + webpack,在这里做一些记录方便以后再看。生成的 dist 目录结构参考的是 vue-cli,源代码也是从 vue-cli 生成的文件中拷贝过来的。
代码和注释上传到了这个仓库,方便查看。
首先配置好 package.json
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26
| { "name": "hello-vue", "version": "1.0.0", "description": "", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { "vue": "^2.6.10" }, "devDependencies": { "babel-loader": "^8.0.6", "file-loader": "^4.2.0", "less-loader": "^5.0.0", "less": "^3.10.3", "style-loader": "^1.0.0", "css-loader": "^3.2.0", "vue-loader": "^15.7.1", "webpack": "^4.41.1", "webpack-cli": "^3.3.9" } }
|
首先可以看到很多 loader 啦,loader 是给 webpack 用的用于转换文件的工具:
loader 用于对模块的源文件(不一定是代码)进行转换。loader 可以使你在 import 或”加载”模块时预处理文件。因此,loader 类似于其他构建工具中的“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript 或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS 文件!
先装一下,我用的是 yarn
:
为了方便,从 vue-cli
生成的文件拷贝 src
目录过来。
现在的目录是这样的:
1 2 3 4 5
| . ├── node_modules ├── package.json ├── src └── yarn.lock
|
然后是配置 webpack.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70
| const path = require('path')
const VueLoaderPlugin = require('vue-loader/lib/plugin')
module.exports = { mode: 'production', entry: path.resolve(__dirname, 'src', 'main.js'), output: { filename: 'js/[name].[chunkhash:12].js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.vue$/, loader: 'vue-loader' }, { test: /\.(png|jpe?g|gif)$/i, use: [ { loader: 'file-loader', options: { name: '[contenthash:12].[ext]', outputPath: 'assets' } } ] }, { test: /\.css$/, use: ['css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.html$/, use: ['html-loader'] }, { test: /\.js$/, loader: 'babel-loader', include: path.resolve(__dirname, 'src'), exclude: file => /node_modules/.test(file) && !/\.vue\.js/.test(file) } ] }, plugins: [ new VueLoaderPlugin() ] }
|
配置了好几个 loader,当 webpack 遇到了符合 test 字段的相应文件结尾的文件时,就会调用对应的 loader 进行转换。
再使用 npx 运行 webpack:
安装新版本 Node.js 的时候也会安装 npx,npx 的作用就是直接执行目录下的 node_modules/.bin/
下的可执行文件,运行 npx webpack
的时候,就等于是执行了 ./node_modules/.bin/webpack
。
当运行 webpack
命令的时候,会默认读取根目录的 webpack.config.js
文件作为配置文件,所以这里不需要传入配置文件的路径。
当然我们用得最多的还是 package.json
的 scripts 字段,等下再配置。
运行以上的命令,如无意外,就能发现多出了一个 dist
目录,但是里面并没有 *.html 文件,因为缺少了生成 html 的插件。
安装 html-webpack-plugin
插件:
1
| $ yarn add html-webpack-plugin --dev
|
webpack.config.js
配置文件中增加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
|
const HtmlWebpackPlugin = require('html-webpack-plugin') ·····
module.exports = { ······ plugins: [ new VueLoaderPlugin(), new HtmlWebpackPlugin({ favicon: path.resolve('src', 'template/favicon.ico'), minify: { collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true }, showErrors: false }) ] }
|
再写一个 scripts
:
1 2 3 4 5 6 7 8
| { ··· "scripts": { "build": "webpack" }, ··· }
|
运行:
这样在生成的 dist
目录中就增加了一个 index.html
文件。
这时候观察 dist
目录中是没有 css
文件的,因为 webpack 默认将 css 文件也打包进 javascript 中,所以下一步是分离 css 文件,需要安装一个 webpack 插件,顺便再装一个清理 dist
目录的插件:
1
| $ yarn add mini-css-extract-plugin clean-webpack-plugin --dev
|
webpack.config.js
配置文件中增加:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const MiniCssExtractPlugin = require('mini-css-extract-plugin') ·····
module.exports = { ······ module: { rules: [ ······ { test: /\.css$/, use: [MiniCssExtractPlugin.loader, 'css-loader'] }, ······ ] }, plugins: [ ······ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ publicPath: '/css/', filename: 'css/[name].[hash:12].css', chunkFilename: 'css/[id].[hash:12].css' }) ] }
|
运行 build
命令,如无意外,dist
目录中会增加了一个 css
目录,并且里面多了一个 *.css
文件,dist/index.html
中也可以看到,引入了 css
的路径。
但是现在的 css
的文件是没有压缩的,我们还需要增加一个插件。
1
| $ yarn add optimize-css-assets-webpack-plugin --dev
|
安装 optimize-css-assets-webpack-plugin
插件用于优化 *.css
文件,详情可以看 官方文档:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
| ······
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') ······
module.exports = { ······ module: { ······ }, plugins: [ ······ ], optimization: { splitChunks: { chunks: 'all' }, minimizer: [new OptimizeCSSAssetsPlugin()] }, }
|
配置好之后,运行 build
命令,如无意外,dist/css
中的 *.css
文件的内容都被优化压缩了,但是同时 JavaScript 有没有被优化压缩,好烦。
查了 文档 发现 webpack4 需要同时定义一个 JS minimizer
。
1
| $ yarn add terser-webpack-plugin --dev
|
引入这个插件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ······
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin') const TerserPlugin = require('terser-webpack-plugin') ······
module.exports = { ······ module: { ······ }, plugins: [ ······ ], optimization: { splitChunks: { chunks: 'all' }, minimizer: [new TerserPlugin(), new OptimizeCSSAssetsPlugin()] }, }
|
运行 build
命令,如无意外,这一次 *.css
和 *.js
文件都会压缩成功了。
这时候打开 dist/index.html
发现文件里面并没有 id 为 #app
的 HTML 元素,而 src/main.js
中将 Vue 应用挂载在 id 为 app
的元素上:
1 2 3
| new Vue({ render: h => h(App) }).$mount('#app')
|
查看 vue-cli 生成的文件中发现 public/index.html
被用作 html-webpack-plugin
插件的模板。
我把这个文件拷贝到 src/template
目录中,顺便把 favicon.ico
也拷贝到这个目录中。我把模板改成 *.hbs
文件,因为我比较会用 handlebars,哈哈。
1
| $ yarn add handlebars-loader handlebars --dev
|
修改配置文件:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| module.exports = { module: { rules: [ ······ { test: /\.hbs$/, use: ['handlebars-loader'] }, ······ ] }, plugins: [ new HtmlWebpackPlugin({ favicon: path.resolve('src', 'template/favicon.ico'), minify: { collapseWhitespace: true, removeComments: true, removeRedundantAttributes: true, removeScriptTypeAttributes: true, removeStyleLinkTypeAttributes: true, useShortDoctype: true }, template: path.resolve('src', 'template/index.hbs'), showErrors: false }) ] } ······
|
运行 build
命令,如无意外,dist/index.html
中就包含了模板中的代码了。
还要区分环境,vue-loader 的文档中也显示了需要区分环境:
1
| $ yarn add cross-env --dev
|
我是用 cross-env
来定义环境变量。
修改 package.json
:
1 2 3 4 5 6 7 8
| { ··· "scripts": { "build:prod": "cross-env NODE_ENV=production webpack --config webpack.config.js --progress" }, ··· }
|
修改 webpack.config.js
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ······ const = process.env.NODE_ENV === 'production' ······
module.exports = { module: { rules: [ ······ { test: /\.css$/, use: [ !isProduction ? 'vue-style-loader' : MiniCssExtractPlugin.loader, 'css-loader' ] }, ······ ] } }
|
webpack 建议使用 webpack.common.js,webpack.dev.js,webpacm.prod.js
区分环境,以后可以自己修改。
另外还可以修改一下构建时输出到控制台的提示格式:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24
| ······
module.exports = { ······ stats: { builtAt: true, assets: true, colors: true, modules: false, timings: true, warnings: true, children: false, entrypoints: false } ······ }
|
最后把 vue-cli 生成的一些配置文件也拷贝过来,包括 .browserslistrc
,.eslintrc.js
,.gitignore
,babel.config.js
。
安装 vue 的 babel preset:
1
| $ yarn add @vue/babel-preset-app --dev
|
这样就大功告成了。
往后如果要添加什么功能,都可以在这个配置的基础上完善。