完成一个VUE项目的Webpack模块化打包实例
项目代码看这里
项目目录解构及构建需求 1 2 3 4 5 6 7 8 9 10 11 12 13 . ├── public │ ├── favicon.ico │ └── index.html └── src ├── App.vue ├── assets │ └── logo.png ├── components │ └── HelloWorld.vue ├── main.js └── style.less
构建需求
开发过程中,使用webpack server作为开发服务器,使用eslint监测代码质量
VUE文件模块化打包
less文件,编译为css文件
js文件,ES6新特性需进行转换。
图片文件需进行压缩
打包后的js文件、css文件、html文件需要压缩
开发环境,/api/xxx
请求需代理至测试服务器http://test.example.com
…..
构建开发过程 环境 webpack
: 5.10.0
node
:v14.2.0
npm
:6.14.4
yarn
:1.22.4
初始准备
安装webpack:yarn add webpack webpack-cli --dev
在根目录创建webpack.common.js
、webpack.dev.js
、webpack.prod.js
分别用于配置公共构建配置、开发环境构架配置、生产环境构建配置
配置webpack基础配置.先在webpack.common.js
配置文件中写入基础配置,先让webpack能够完成最基础的打包构建,最后优化时再进行配置文件分割。
1 2 3 4 5 6 7 8 9 10 let path = require ('path' )module .exports = { mode:"none" , entry:"./src/main.js" , output:{ path:path.join(__dirname,'dist' ), filename:"[name].js" }, }
修改package.json
, script
命令,方便构建打包
1 2 3 4 5 6 { "script" :{ "dev" :"webpack --config webpack.common.js" , "build" :"webpack --config webpack.build.js" , } }
资源加载器配置 vue 加载器 安装 vue-loader
,vue-template-compiler
1 yarn add vue-loader vue-template -compiler --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 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )module .exports = { mode:"none" , entry:"./src/main.js" , output:{ path:path.join(__dirname,'dist' ), filename:"[name].js" }, module :{ rules:[ { test: /\.vue$/ , loader: 'vue-loader' } ] }, plugins:[ new vueLoaderPlugin() ] }
less && css 安装less
, less-loader
,css-loader
,style-loader
1 yarn add less less-loader css-loader style-loader --dev
配置 loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )module .exports = { module :{ rules:[ { test: /\.less$/ , use:['style-loader' ,'css-loader' ,'less-loader' ] }, { test: /\.css$/ , use:['style-loader' ,'css-loader' ] } ] }, plugins:[ ] }
图片资源加载 安装
1 yarn add url-loader file-loader --dev
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )module .exports = { module :{ rules:[ { test: /\.(png|jpg|gif)$/ , use:{ loader:'url-loader' , options:{ limit:10 * 1024 } } } ] } }
ES6新特性转换 安装babel-loader
,@babel/core
,@babel/preset-env
1 yarn add babel-loader @babel/core @babel/preset-env --dev
配置loader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )module .exports = { module :{ rules:[ { test: /.\js/ , use:{ loader:'babel-loader' , options:{ presets:['@babel/preset-env' ] } } } ] }, }
其他自动化处理 静态资源文件构建处理 安装 copy-webpack-plugin
1 yarn add copy-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 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )const CopyWebpackPlugin = require ('copy-webpack-plugin' )module .exports = { module :{ rules:[ ] }, plugins:[ new vueLoaderPlugin(), new CopyWebpackPlugin({ patterns:[ { from :'public/favicon.ico' , } ] }), ] }
处理html文件 1. 将bundle.js自动填充到html 安装 html-webpack-plugin
1 yarn add html-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 25 26 27 28 let path = require ('path' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )const CopyWebpackPlugin = require ('copy-webpack-plugin' )module .exports = { module :{ rules:[ ] }, plugins:[ new vueLoaderPlugin(), new CopyWebpackPlugin({ patterns:[ { from :'public/favicon.ico' , } ] }), new HtmlWebpackPlugin({ template:path.join(__dirname,'public/index.html' ), title:'VUE Webpack Prodtion' }) ] }
2. 处理BASE_URL BASE_URL
使用的是process.env
的值。也可以共享webpack在plugins中DefinePlugin的值。
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 let path = require ('path' )const webpack = require ('webpack' )const vueLoaderPlugin = require ('vue-loader/lib/plugin' )const CopyWebpackPlugin = require ('copy-webpack-plugin' )const HtmlWebpackPlugin = require ('html-webpack-plugin' )module .exports = { module :{ rules:[ ] }, plugins:[ new vueLoaderPlugin(), new CopyWebpackPlugin({ patterns:[ { from :'public/favicon.ico' , } ] }), new webpack.DefinePlugin({ BASE_URL: '"/"' }), new HtmlWebpackPlugin({ template:path.join(__dirname,'public/index.html' ), title:'VUE Webpack Prodtion' }) ] }
添加ESLint 安装
1 yarn add eslint eslint-loader eslint-plugin -vue babel-eslint --dev
配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 module .exports = { module :{ rules:[ { enforce: 'pre' , test: /\.(js|vue)$/ , loader: 'eslint-loader' , exclude: /node_modules/ } ] }, }
修改package.json
1 2 3 4 5 { "script" :{ "lint" :"eslint --ext .js --ext .vue src/" } }
添加.eslintrc
1 2 3 4 5 module.exports = { extends: [ "plugin:vue/essential" ] }
使用webpack-dev-server 启动服务 安装
1 yarn add webpack-dev -server --dev
配置
1 2 3 4 5 6 7 module .exports = { devServer:{ contentBase:path.join(__dirname,'dist' ) }, }
修改packaje.json
1 2 3 4 5 6 7 { "scripts" : { "dev" : "webpack serve --config webpack.common.js" , "build" : "webpack --config webpack.build.js" , "lint" : "echo \"请使用 ESLint 实现此任务\"" }, }
运行yarn dev ,启动服务器,发现浏览器报错“http://localhost:8080/[Object%20Module]”
这个问题是由于图片文件使用ES Module打包引起的。
修改url-loader
配置
1 2 3 4 5 6 7 8 9 10 { test: /\.(png|jpg|gif)$/ , use:{ loader:'url-loader' , options:{ limit:10 * 1024 , esModule: false , } } },
问题2: 修改代码,自动编译但浏览器不会自动更新
添加target配置,告诉webpack,代码运行环境
1 2 3 module .export = { target: 'web' }
环境配置 到目前为止,我们的基础的webpack打包配置已经完成。
现在,我们需要根据环境不同,配置不同的环境变量。
环境变量合并
使用webpack-merge
来合并公共配置与开发配置和生产配置。
安装
1 yarn add webpack-merge --dev
webpack.dev.js
1 2 3 4 5 6 7 8 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )module .exports = merge(common,{ mode:'development' , target:'web' , })
webpack.prod.js
1 2 3 4 5 6 7 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )module .exports = merge(common,{ mode:'production' })
修改package.json
1 2 3 4 5 6 7 { "scripts" : { "dev" : "webpack serve --config webpack.dev.js" , "build" : "webpack --config webpack.build.js" , "lint" : "echo \"请使用 ESLint 实现此任务\"" }, }
优化开发环境配置 添加代理 使用webpack-dev-server
的proxy 将api请求代理到测试服务器http://test.example.com
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )module .exports = merge(common,{ mode:'development' , target:'web' , devServer:{ proxy:{ '/api' :{ target:'http://test.example.com' , pathRewrite:{ '^/api' :'' }, changeOrigin:true } }, } })
添加热加载(HMR) vue-loader
内部实现了vue-hot-reload-api
,当webpack开启了热更新之后,更新vue模块代码时,vue-loader
会自动处理热更新。
启动HMR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )module .exports = merge(common,{ mode:'development' , target:'web' , devServer:{ proxy:{ '/api' :{ target:'http://test.example.com' , pathRewrite:{ '^/api' :'' }, changeOrigin:true , } }, hot:true }, })
当父子组件数据传递时,更新父组件数据,会更新父组件和子组件。若只想更新子组件:可以使用vue-hot-reload-api配置vue热加载
安装
1 yarn add vue-hot -reload -api --dev
配置使用
VUE文件
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 <template> <div id="app"> <img alt="Vue logo" src="./assets/logo.png"> <HelloWorld msg="Welcome to Your Vue.js App"/> <input type="text"> </div> </template> <script> import HelloWorld from './components/HelloWorld.vue' if (module.hot) { const api = require('vue-hot-reload-api') const Vue = require('vue') api.install(Vue) if (!api.compatible) { throw new Error('vue-hot-reload-api is not compatible with the version of Vue you are using.') } module.hot.accept() if (!module.hot.data) { api.createRecord('very-unique-id', HelloWorld) } else { api.rerender('very-unique-id', HelloWorld) } } export default { name: 'App', components: { HelloWorld }, } </script>
此时,启动webpack server,当更新App.vue中msg的数据时,就只会更新HelloWorld模块,而不是刷新整个页面了。
优化生产环境配置 输出文件添加hash 1 2 3 4 5 6 7 8 9 10 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )module .exports = merge(common,{ mode:'production' , output:{ filename:"[name]-[hash:8].js" }, })
编译前清空dist目录 安装 clean-webpack-plugin
1 yarn add clean-webpack -plugin --dev
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 const {merge} = require ('webpack-merge' )const common = require ('./webpack.common.js' )const {CleanWebpackPlugin} = require ('clean-webpack-plugin' )module .exports = merge(common,{ mode:'production' , output:{ filename:"[name]-[hash:8].js" }, plugins:[ new CleanWebpackPlugin() ] })
拆分css文件 安装 mini-css-extract-plugin
1 yarn add mini-css -extract -plugin --dev
webpack.common.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 const MiniCssExtractPlugin = require ('mini-css-extract-plugin' )module .exports={ module :{ rules:[ { test: /\.css$/ , use:[MiniCssExtractPlugin.loader,'css-loader' ] }, { test: /\.less$/ , use:[MiniCssExtractPlugin.loader,'css-loader' ,'less-loader' ] }, } } }
webpack.prod.js
1 2 3 4 5 6 7 8 9 10 11 12 const MiniCssExtractPlugin = require ('mini-css-extract-plugin' )module .exports={ module :{ }, plugins:[ new MiniCssExtractPlugin({ filename:'[name]-[hash:8].css' }) ] }
压缩css
安装optimize-css-assets-webpack-plugin
1 yarn add optimize-css -assets -webpack -plugin --dev
1 2 3 4 5 6 7 8 9 10 11 12 13 14 const MiniCssExtractPlugin = require ('mini-css-extract-plugin' )const OptimizeCssAssetsWebpackPlugin = require ('optimize-css-assets-webpack-plugin' )module .exports={ module :{ }, plugins:[ new MiniCssExtractPlugin({ filename:'[name]-[hash:8].css' }), new OptimizeCssAssetsWebpackPlugin() ] }
添加tree shaking 1 2 3 4 5 optimization:{ usedExports:true , concatenateModules:true , minimize:true , },