前言:都2020年了,感受是时候该学一波webpack了,趁着最近有时间,就学了一下,等把官网上的webpack结构和模块大概看了一遍以后,就感受能够开始搭个项目实战一下了,从0开始,一步步记录下来
使用版本: webpack4.xcss
* html: html-webpack-plugin clean-webpack-plugin * css: style-loader css-loader sass-loader node-sass postcss-loader autoprefixer * js: babel-loader @babel/preset-env @babel/core @babel/polyfill core-js@2 @babel/plugin-transform-runtime @babel/runtime @babel/runtime-corejs2 * vue: vue-loader vue-template-compiler vue-style-loader vue vue-router axios vuex * webpack: file-loader url-loader webpack-dev-server webpack-merge copy-webpack-plugin happypack HardSourceWebpackPlugin webpack-bundle-analyzer optimize-css-assets-webpack-plugin portfinder FriendlyErrorsPlugin另外要注意 :光理论是不够的。在此赠送2020最新企业级 Vue3.0/Js/ES6/TS/React/node等实战视频教程,想学的可进裙 519293536 免费获取,小白勿进哦
-- 生成hmtl模板 -- 删除上一次的dist文件 -- 自动添加浏览器前缀 -- 使用sass预编译器 -- 转换ES6,7,8,9语法为ES5 -- 大于10k文件打包到dist,小于10k转换为base64 -- 兼容vue-- 拷贝静态文件 -- 热更新 -- 区分当前环境 -- 多线程打包 -- 缓存未改变模块 -- g-zip压缩 -- 获取本机ip -- 打包大小分析 -- 压缩css -- 检查端口是否存在复制代码
1. 新建一个文件 取名为webpack-vue 2. cd webpack-vue npm init -y npm i -D webpack webpack-cli 3. 新建 src/main.js ,里面随便写点 console.log('hello,webpack') 4. 修改 package.json - >scripts ,添加 "build":"webpack src/main.js" 5. 而后 npm run build 若是多了一个dist文件,那么初次打包就成功了复制代码
const path=require('path') module.exports = { mode:'development', entry:path.resolve(__dirname,'../src/main.js'), //入口文件 output:{ filename:'[name].[hash:8].js', //打包后的名字 生成8位数的hash path.resolve(__dirname,'../dist') //打包的路径 } } 而后修改 package.json ->scripts,修改成: "build":"webpack --config build/webpack.config.js" 而后npm run build复制代码
3. 咱们生成了main.js以后,不可能每次都手动在index.html里面引入,因此咱们须要这个插件来帮咱们自动引入html
先安装插件: vue
npm i -D html-webpack-plugin复制代码
根目录新建一个 public/index.htmlnode
修改webpack.config.js:jquery
const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin') //这里引入插件 module.exports = { mode:'development', // 开发模式 entry: path.resolve(__dirname,'../src/main.js'), // 入口文件 output: { filename: '[name].[hash].js', // 打包后的文件名称 path: path.resolve(__dirname,'../dist') // 打包后的目录 }, //插件注入 plugins:[ new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html') }) ] }复制代码
而后npm run build 就会发现dist里面多了index.html,而且已经自动帮咱们引入了main.jswebpack
4. 因为hash每次生成的不一样,致使每次打包都会将新的main.js打包到dist文件夹,因此咱们须要一个插件来打包前删除dist文件ios
先安装插件: nginx
npm i -D clean-webpack-plugin复制代码
webpack.config.js const {CleanWebpackPlugin} = require('clean-webpack-plugin') //引入 plugins:[ new HtmlWebpackPlugin({ template:path.resolve(__dirname,'../public/index.html') }), new CleanWebpackPlugin() ]复制代码
5.咱们通常把不须要打包的静态文件放在public里面,这个地方不会被打包到dist,因此咱们须要使用插件来把这些文件复制过去web
先安装插件:vue-router
npm i -D copy-webpack-plugin复制代码
webpack.config.js const CopyWebpackPlugin = require('copy-webpack-plugin') // 复制文件 plugins: [ new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist') } ] }) ]复制代码
6. 为了让webpack识别css,咱们须要安装loader,并将解析后的css插入到index.html里面的style
先安装:
npm i -D style-loader css-loader复制代码
webpack.config.js module.exports = { module:{ rules:[{ test:/\.css$/, use:['style-loader','css-loader'] // 从右向左解析原则 }] } }复制代码
7.咱们这里可使用预编译器更好的处理css,我这里使用的是sass
先安装:
npm install -D sass-loader node-sass
webpack.config.js module:{ rules: [{ test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] // 从右向左解析原则 }] }复制代码
8. 自动添加浏览器前缀
先安装:
npm i -D postcss-loader autoprefixer
webpakc.config.js module: { rules: [ { test: /\.scss$/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] // 从右向左解析原则 sass-loader必须写后面 }] } 在根目录新建 .browserslistrc 这个里面配置兼容的浏览器 这里贴下个人默认配置,能够根据实际状况本身去配置 > 1% last 2 versions not ie <= 8 再新建一个postcss.config.js module.exports = { plugins: [require('autoprefixer')] // 引用该插件便可了 } 这样打包后就自动帮你添加浏览器前缀了复制代码
9.以前的style-loader只是把css打包到index.html里面的style-loader里面,若是css特别多这种办法确定不行,因此咱们须要单独抽离提取css
先安装:
npm i -D mini-css-extract-plugin
webpack.config.js const MiniCssExtractPlugin = require("mini-css-extract-plugin") //提取css module: { rules: [{ test: /\.scss$/, use: [MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader'] // 从右向左解析原则 }] } plugins: [ new MiniCssExtractPlugin({ filename: "css/[name].[hash:8].css", chunkFilename: "[id].css", }) ] 这样打包后就将css打包到css文件下里面了复制代码
10. 咱们为了减小图片字体等打包的大小,咱们可使用url-loader将少于指定大小的文件转换为base64,使用file-loader将大于指定大小的文件 移动到指定的位置
先安装:
npm i -D file-loader url-loader
webpack.config.js module:{ rules:[ { test:/\.(jpe?g|png|gif|svg)(\?.*)?$/ //图片 use:[{ loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader', options:{ name:'img/[name].[hash:8].[ext]', publicPath: '../' //为了防止图片路径错误 } } } }] },{ test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, use: [{ loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]', publicPath: '../' } } } }] },{ test:/\.(woff2?|eot|ttf|otf)(\?.*)$/, loader:'url-loader', options:{ limit:10240, fallback:{ loader:'file-loader, options:{ name:'fonts/[name].[hash:8].[ext]', publicPath: '../' } } } } ] } webpack只会转换和移动项目中使用了的图片 打包后发现图片字体等也打包进去了复制代码
11.为了兼容浏览器,咱们须要将ES6,7,8,9转换为Es5
先安装:
npm install -D babel-loader @babel/preset-env @babel/core复制代码
webpack.config.js module: { rules: [{ test: /\.js$/, use: ['babel-loader'] }] } 根目录新建 .babelrc { "presets": [ "@babel/preset-env" ] } 一些新的api如Promise,set,Maps等还不支持转换,因此咱们须要另外一个插件babel/polyfill,可是这个插件会将全部的poly都打包到 mian.js里面,因此咱们须要另外一个插件 core-js@2 来按需加载 `npm i -s @babel/polyfill core-js@2` 修改.babelrc { "presets": [ [ "@babel/preset-env", { "modules": false, "useBuiltIns": "usage", "corejs": 2, "targets": { "browsers": [ "last 2 versions", "ie >= 10" ] } } ] ] } 还有一个问题,会影响全局依赖,因此咱们须要另外一个插件来解决这个问题 npm i @babel/plugin-transform-runtime -D npm i --save @babel/runtime npm i --save @babel/runtime-corejs2 修改.babelrc { "presets": [ "@babel/preset-env" ], "plugins": [ [ "@babel/plugin-transform-runtime", { "helpers": true, "regenerator": true, "useESModules": false, "corejs": 2 } ] ] } 这样就完美解决ES6新api的兼容问题了复制代码
12.兼容vue
先安装:
npm i -D vue-loader vue-template-compiler vue-style-loader npm i -S vue 复制代码
webpack.config.js const vueLoaderPlugin=require('vue-loader/lib/plugin') function resolve(dir) { return path.join(__dirname, '..', dir) } module:{ rules:[{ test:/\.vue$/, use:[vue-loader] }] }, resolve:{ alias:{ 'vue$':'vue/dist/vue.runtime.esm.js', '@':path.resolve(__dirname,'../src') }, extensions: ['.js', '.vue', '.json'], }, plugins:[ new vueLoaderPlugin() ] 这样配置完成以后就能够webpack就识别了vue文件复制代码
13.热更新
先安装:
npm i -D webpack-dev-server复制代码
wepack.config.js const webpack = require('webpack') devServer: { compress: true, port: 8989, hot: true, inline: true, hotOnly: true, //当编译失败时,不刷新页面 overlay: true, //用来在编译出错的时候,在浏览器页面上显示错误 publicPath: '/', //必定要加 open: true, watchOptions: { // 不监听的文件或文件夹,支持正则匹配 ignored: /node_modules/, // 监听到变化后等1s再去执行动做 aggregateTimeout: 1000, // 默认每秒询问1000次 poll: 1000 } }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.NoEmitOnErrorsPlugin() ] 在package.json里面配置: "dev":"webpack-dev-server --config build/webpack.config.js" 在main.js里面 import Vue from "vue" import App from "./App" new Vue({ render:h=>h(App) }).$mount('#app') src文件夹内新建一个APP.vue,内容自定义 而后npm run dev 页面就运行起来了复制代码
14.区分开发环境和生产环境
build文件夹内新建 webpack.dev.js webpack.prod.js复制代码
开发环境: 1.不须要压缩代码 2.热更新 3.完整的soureMap ... 生产环境: 1.压缩代码 2.提取css文件 3.去除soureMap (根据我的须要) ...复制代码
咱们这里须要安装 webpack-merge 来合并配置项
先安装:
npm i -D webpack-merge 复制代码
webpack.dev.js const webpackConfig = require('./webpack.config') const merge = require('webpack-merge') const webpack = require('webpack') module.exports = merge(webpackConfig, { mode: 'development', devtool: 'cheap-module-eval-source-map', devServer: { compress: true, port: 8989, hot: true, inline: true, hotOnly: true, //当编译失败时,不刷新页面 overlay: true, //用来在编译出错的时候,在浏览器页面上显示错误 publicPath: '/', //必定要加 open: true, watchOptions: { // 不监听的文件或文件夹,支持正则匹配 ignored: /node_modules/, // 监听到变化后等1s再去执行动做 aggregateTimeout: 1000, // 默认每秒询问1000次 poll: 1000 } }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] }, { test: /\.css$/, use: ['vue-style-loader', 'css-loader', 'postcss-loader'], }, { test: /\.scss$/, use: ['vue-style-loader', 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ } ] }, plugins: [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), new webpack.NoEmitOnErrorsPlugin() ] })复制代码
webpack.prod.js const webpackConfig = require('./webpack.config') const merge = require('webpack-merge') const path = require('path') const { CleanWebpackPlugin } = require('clean-webpack-plugin') //清除dist const MiniCssExtractPlugin = require("mini-css-extract-plugin") //提取cssfunction resolve(dir) { return path.join(__dirname, '..', dir) } module.exports = merge(webpackConfig, { mode: "production", devtool: 'none', optimization: { splitChunks: { chunks: 'all', cacheGroups: { vendors: { name: 'vendors', test: /[\\\/]node_modules[\\\/]/, priority: -10, chunks: 'initial' }, common: { name: 'chunk-common', minChunks: 2, priority: -20, chunks: 'initial', reuseExistingChunk: true } } } }, module: { rules: [ { test: /\.js$/, use: ['babel-loader'] exclude: /node_modules/, include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')] }, { test: /\.css$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', } }, 'css-loader', 'postcss-loader'], }, { test: /\.scss$/, use: [ { loader: MiniCssExtractPlugin.loader, options: { publicPath: '../', } }, 'css-loader', 'postcss-loader', 'sass-loader'], exclude: /node_modules/ } ] }, plugins: [ new CleanWebpackPlugin(), new MiniCssExtractPlugin({ filename: 'css/[name].[hash].css', chunkFilename: 'css/[name].[hash].css', }) ] })复制代码
webpack.config.js const path = require('path') const HtmlWebpackPlugin = require('html-webpack-plugin') //这里引入插件 const vueLoaderPlugin = require('vue-loader/lib/plugin') const CopyWebpackPlugin = require('copy-webpack-plugin') // 复制文件function resolve(dir) { return path.join(__dirname, '..', dir) } module.exports = { mode: 'development', entry: path.resolve(__dirname, '../src/main.js'), output: { filename: 'js/[name].[hash:8].js', path: path.resolve(__dirname, '../dist'), chunkFilename: 'js/[name].[hash:8].js', //异步加载模块 publicPath: './' }, externals: {}, module: { noParse: /jquery/, rules: [ { test: /\.vue$/, use: [{ loader: 'vue-loader', options: { compilerOptions: { preserveWhitespace: false } } }] }, { test: /\.(jpe?g|png|gif)$/i, //图片文件 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'img/[name].[hash:8].[ext]', publicPath: '../' } } } } ] }, { test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)(\?.*)?$/, //媒体文件 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'media/[name].[hash:8].[ext]', publicPath: '../' } } } } ] }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/i, // 字体 use: [ { loader: 'url-loader', options: { limit: 10240, fallback: { loader: 'file-loader', options: { name: 'font/[name].[hash:8].[ext]', publicPath: '../' } } } } ] } ] }, resolve: { alias: { 'vue$': 'vue/dist/vue.esm.js', '@': resolve('src'), }, extensions: ['.js', '.vue', '.json'], }, //插件注入 plugins: [ new HtmlWebpackPlugin({ template: path.resolve(__dirname, '../public/index.html') }), new vueLoaderPlugin(), new CopyWebpackPlugin({ patterns: [ { from: path.resolve(__dirname, '../public'), to: path.resolve(__dirname, '../dist') } ] }) ] }复制代码
默认为production,webpack4.x默认会压缩代码和去除无用的代码
可选参数:production, development
ps:以前我认为只须要设置mode为production,就不用使用压缩css和js的插件,但结果发现我错了,仔细比较了下,仍是要安装的
先安装打包css的:
npm i -D optimize-css-assets-webpack-plugin复制代码
webpack.prod.js const optimizeCss = require('optimize-css-assets-webpack-plugin'); plugins:[ new optimizeCss({ cssProcessor: require('cssnano'), //引入cssnano配置压缩选项 cssProcessorOptions: { discardComments: { removeAll: true } }, canPrint: true //是否将插件信息打印到控制台 }) ]复制代码
压缩js和js打包多线程的暂时没有添加,在网上搜有的说不用添加,有的说仍是要安装插件,等实际项目中我用完以后再来添加
alias 能够告诉webpack去指定文件夹去寻找,尽可能多使用 include exclude 包括和过滤 noParse 当咱们代码中使用到import jq from 'jquery'时,webpack会去解析jq这个库是否有依赖其余的包。这个能够告诉webpack没必要解析 extensions 使用频率高的写在前面 复制代码
webpack处理文本,图片,css的时候,因为js单线程的特性,只能一个一个文件的处理,HappyPack能够将这个任务 分解到多个子线程里面,子线程完毕后会把结果发送到主线程,从而加快打包速度
先安装:
npm i -D happypack
webpack.prod.js const HappyPack = require('happypack') //单进程转多进程 const os = require('os') const happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }) module: { rules: [{ test: /\.js$/, use: ['happypack/loader?id=happyBabel'], exclude: /node_modules/, include: [resolve('src'), resolve('node_modules/webpack-dev-server/client')] }] } plugins:[ new HappyPack({ id: 'happyBabel', loaders: ['babel-loader?cacheDirectory'], threadPool: happyThreadPool }) ]复制代码
将不怎么改变的第三方依赖,咱们能够用DllPlugin DllReferencePlugin将它从依赖中抽离出来,这样每一次打包就不用打包这些文件,加快了打包的速度;
可是webpack4.x的性能已经很好了,参考vue-cli也没有使用dll抽离,因此咱们这里也不使用了,这里咱们使用 另外一个插件:hard-source-webpack-plugin ,这个插件会去对比修改了哪些配置,只去打包修改过了的配置 第一次打包速度正常,第二次打包速度能提高 50%+
npm i -D hard-source-webpack-plugin webpack.prod.js const HardSourceWebpackPlugin = require('hard-source-webpack-plugin') //缓存第三方模块 plugins: [ new HardSourceWebpackPlugin() ]复制代码
经过cdn加载的依赖,能够在这里设置,就不会经过webpack编译
g-zip压缩能够将已经压缩过的js,css再次压缩一遍,减小了打包大小,须要nginx配置
npm i -D compression-webpack-plugin webpack.prod.js const CompressionWebpackPlugin = require('compression-webpack-plugin') const productionGzipExtensions = ["js", "css"]; plugins:[ new CompressionWebpackPlugin({ filename: '[path].gz[query]', algorithm: 'gzip', test: new RegExp("\\.(" + productionGzipExtensions.join("|") + ")$"), threshold: 10240, // 只有大小大于10k的资源会被处理 minRatio: 0.6 // 压缩比例,值为0 ~ 1 }) ]复制代码
webpack.dev.js const os = require('os') devServer:{ host:()=>{ var netWork = os.networkInterfaces() var ip = '' for (var dev in netWork) { netWork[dev].forEach(function (details) { if (ip === '' && details.family === 'IPv4' && !details.internal) { ip = details.address return; } }) } return ip || 'localhost' } }复制代码
根目录新建vue.config.js 里面配置一些公共的配置如:
const os = require('os') module.exports = { dev: { host: getNetworkIp(), //端口号 port: 8999, autoOpen: true, //自动打开 }, build: { productionGzipExtensions: ["js", "css"], //须要开启g-zip的文件后缀 productionGzip: false //是否开启g-zip压缩 } } //获取本地ip地址function getNetworkIp() { var netWork = os.networkInterfaces() var ip = '' for (var dev in netWork) { netWork[dev].forEach(function (details) { if (ip === '' && details.family === 'IPv4' && !details.internal) { ip = details.address return; } }) } return ip || 'localhost' } 而后webpack.dev.js webpack.prod.js引入这个文件,获取其中的配置就行了复制代码
先安装:
npm i -D webpack-bundle-analyzer
webpack.prod.jsif (process.env.npm_config_report) { const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin prodConfig.plugins.push( new BundleAnalyzerPlugin() ) } 而后 npm run build --report就会弹出一个页面,里面就是打包大小分析复制代码
先安装:
npm i -S vue-router axios vuex
而后在src里面新建 -> router文件夹 ->新建index.js
index.js import Vue from "vue" import Router from "vue-router" Vue.use(Router)export default new Router({ mode: 'hash', routes: [ { path: '/', name: 'home', component: () => import(/* webpackChunkName: "home" */ "@/views/home"), }, ] }) main.js import router from "./router" new Vue({ router, render: h => h(App) }).$mount('#app') 新建views -> Home.vue 随便写点东西,而后npm run dev 这样就完成了一个路由了复制代码