前面咱們說了webpack的一些基礎,如今咱們來使用webpack實際來編譯寫個項目。
用vue-cli建立一個項目,而後把它的vue-cli-service以及webpack等黑盒工具移除,而後咱們來本身編譯它。
首先咱們要建立三個文件css
首先咱們來編寫webpack.common文件html
const HtmlWebpackPlugin = require('html-webpack-plugin')// html模板 const webpack = require('webpack') const PreloadWebpackPlugin = require('preload-webpack-plugin')// 預加載 const path = require('path') const os = require('os') const VueLoaderPlugin = require('vue-loader/lib/plugin')// vue對應插件 const workers = { // 多線程編譯 loader: 'thread-loader', options: { workers: os.cpus().length } } module.exports = { // entry: ['babel-polyfill', './src/main.js'], entry: './src/main.js', // 入口 output: { filename: 'bundle.js' // 輸出 }, optimization: { concatenateModules: true, // 儘量合併模塊到一個函數 splitChunks: { // 公共代碼拆分 // include all types of chunks chunks: 'all' }, runtimeChunk: true // 運行時代碼 拆出來 緩存 }, resolve: { // modules: [//解析時搜索得模塊,配了致使ie報錯 // path.resolve(__dirname, 'node_modules'), // ], alias: { // 配置別名 components: path.resolve(__dirname, 'src/components'), src: path.resolve(__dirname, 'src') }, extensions: ['.js', '.json', '.vue'] // 省略後綴 }, module: { rules: [ { // babel test: /\.js$/, exclude: /node_modules/, use: [ workers, { loader: 'babel-loader', options: { // babel-polyfill按需引入,差了1m左右 core polyfill原型 presets: [['@babel/preset-env', { useBuiltIns: 'usage', corejs: 3 }]], cacheDirectory: true, // 開啓緩存 第二次構建時,會讀取以前的緩存,吃內存,不開發了記得清下佔用 // presets: ['@babel/preset-env'] plugins: [ // import()語法支持 '@babel/plugin-syntax-dynamic-import' ] } } ] }, { test: /\.(gif|png|jpe?g|svg)$/i, // 不超過10kb轉爲data:urlbase64 use: [ { loader: 'url-loader', options: { limit: 10 * 1024, // 10 KB name: 'image/[name]-[contenthash:8].[ext]', // 輸出hash esModule: false } }] }, { test: /\.vue$/, // 解析vue文件 use: [ workers, 'vue-loader' ] } ] }, plugins: [ new PreloadWebpackPlugin({ // 加入預解析和預加載webpack4以後下載preload-webpack-plugin@next最新版 rel: 'preload', as (entry) { console.log(entry) if (/\.css$/.test(entry)) return 'style' if (/\.woff$/.test(entry)) return 'font' if (/\.(png|jpe?g|gif)$/.test(entry)) return 'image' return 'script' }, include: 'allChunks', fileBlacklist: [/\.(map)$/, /runtime~.+\.js$/] // 把runtime文件抽離出來,由於import()運行時文件變化了,runtime管理運行時babel導入文件的hash也會變化, // 默認webpack打包出來的依賴入口文件會把runtime導入,這樣會致使它的hash也會變化,這樣就會致使緩存失效, // 因此把runtime這個文件加載到html中,從以來入口文件中抽離,來避免依賴入口的hash變化。這樣它就不須要進行預加載了。 }), new HtmlWebpackPlugin({ // html模板 title: 'Vue dev App', template: path.resolve(__dirname, 'public/index.html') }), new VueLoaderPlugin(), new webpack.DefinePlugin({ // 值要求的是一個代碼片斷 BASE_URL: '"/"' }) ] }
代碼裏都有註釋,而後咱們作下思路分析:vue
)java
而後再配置開發階段配置webpack.devnode
const { merge } = require('webpack-merge')// 合併配置插件 const common = require('./webpack.common')// 公共配置 const path = require('path') const os = require('os') const webpack = require('webpack') const workers = { // 多線程編譯 loader: 'thread-loader', options: { workers: os.cpus().length } } module.exports = merge(common, { mode: 'development', devtool: 'eval-cheap-module-source-map', // devtool:'source-map', devServer: { hot: true, contentBase: path.join(__dirname, 'public'), open: true, stats: 'errors-only', clientLogLevel: 'none', disableHostCheck: true }, module: { rules: [ { test: /\.(js|vue)$/, // eslint enforce: 'pre', exclude: /node_modules/, include: path.resolve(__dirname, 'src'), loader: [workers, 'eslint-loader'] }, { test: /\.less$/, // 解析引入得less use: [ workers, 'style-loader', 'css-loader', 'postcss-loader', 'less-loader' ] }, { test: /\.css$/, // 解析組件內部style use: [ workers, 'style-loader', 'css-loader', 'postcss-loader' ] } ] }, plugins: [ new webpack.HotModuleReplacementPlugin() // 熱加載 ] })
postcss.config.jswebpack
const postcssConfig = { plugins: [ // 配合package的browserslist require('postcss-import'), require('postcss-cssnext'), require('cssnano') ] } console.log(process.env.NODE_ENV) if (process.env.NODE_ENV === 'production') { // 使用postcss插件方式實現,webpack插件方式太麻煩了 const purgecss = require('@fullhuman/postcss-purgecss') const purgecssConfig = require('./purgecss.config') postcssConfig.plugins.push(purgecss(purgecssConfig)) } module.exports = postcssConfig
這個裏面引入了一個生產階段對vue內部style css搖樹的插件配置,配置我貼出來
purgecss.configgit
module.exports = { content: ['./public/**/*.html', './src/**/*.vue'], /// / 清理範圍,全部的vue component和和入口的html,js裏引入的默認是全局使用的 defaultExtractor (content) { const contentWithoutStyleBlocks = content.replace(/<style[^]+?<\/style>/gi, '') return contentWithoutStyleBlocks.match(/[A-Za-z0-9-_/:]*[A-Za-z0-9-_/]+/g) || [] }, whitelist: [/el-.+$/], // 將elementuijs使用上的類也放在白名單不做處理 whitelistPatterns: [/-(leave|enter|appear)(|-(to|from|active))$/, /^(?!(|.*?:)cursor-move).+-move$/, /^router-link(|-exact)-active$/, /data-v-.*/, /el-.+$/] }
最後咱們配置生產文件
webpack.prodweb
const { merge } = require('webpack-merge') const { CleanWebpackPlugin } = require('clean-webpack-plugin')// 打包以前清空 const CopyWebpackPlugin = require('copy-webpack-plugin')// copy文件插件 const MiniCssExtractPlugin = require('mini-css-extract-plugin')// 把css抽成link的方式 const OptimizeCssAssetsWebpackPlugin = require('optimize-css-assets-webpack-plugin')// 壓縮css const TerserWebpackPlugin = require('terser-webpack-plugin')// 壓縮js const HtmlWebpackPlugin = require('html-webpack-plugin') const ScriptExtHtmlWebpackPlugin = require('script-ext-html-webpack-plugin')// 匹配 runtime.chunk文件寫到html中 const common = require('./webpack.common') const path = require('path') const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') // webpack 打包結果分析 console.log(common) // common.module.rules.push({ // test: /\.(gif|png|jpe?g|svg)$/i, // //這個依賴下載要用 cnpm 淘寶源,而且用管理員權限下載,下不下來把依賴都刪了,而後從新下載,其餘都下不來,親身經歷 // //算了不用它了,經過它壓縮事後的圖片包括base64,在ie中不能解析 // loader: 'image-webpack-loader', // options: { // mozjpeg: { // progressive: true, // }, // // optipng.enabled: false will disable optipng // optipng: { // enabled: false, // }, // pngquant: { // quality: [0.65, 0.90], // speed: 4 // }, // gifsicle: { // interlaced: false, // }, // // the webp option will enable WEBP // webp: { // quality: 75 // } // } // }) module.exports = merge(common, { mode: 'production', output: { path: path.resolve(__dirname, 'dist'), filename: '[name].[contenthash:8].bundle.js', // 輸出name hash chunkFilename: '[name].[contenthash:8].chunk.js' // chunk文件 hash }, devtool: 'none', module: { rules: [ { test: /\.(less)$/, use: [ MiniCssExtractPlugin.loader, // 生成環境,最後不適用style插入,使用link方式插入 // 'style-loader', 'css-loader', 'postcss-loader', 'less-loader' ] }, { test: /\.css$/, // 解析組件內部style use: [ MiniCssExtractPlugin.loader, // 'style-loader', 'css-loader', 'postcss-loader' ] } ] }, optimization: { minimizer: [ new TerserWebpackPlugin({ parallel: true// 開啓多線程 }), // 壓縮js new OptimizeCssAssetsWebpackPlugin({ cssProcessPluginOptions: { preset: ['default', { discardComments: { removeAll: true } }] } }) // 壓縮css ], // 模塊只導出被使用的成員 usedExports: true, sideEffects: true // 標記無反作用,配合package js搖樹使用 }, plugins: [ new CleanWebpackPlugin(), new HtmlWebpackPlugin({ title: 'Vue prod App', template: path.resolve(__dirname, 'public/index.html'), minify: { removeComments: true, collapseWhitespace: true, removeRedundantAttributes: true, useShortDoctype: true, removeEmptyAttributes: true, removeStyleLinkTypeAttributes: true, keepClosingSlash: true, minifyJS: true, minifyCSS: true, minifyURLs: true } }), new MiniCssExtractPlugin({ // 生成link的css文件,名稱hash filename: '[name].[contenthash:8].css' }), new CopyWebpackPlugin([ // 複製文件 'public' ]), new ScriptExtHtmlWebpackPlugin({ // 寫入html中 inline: [/runtime~.+\.js$/] // 正則匹配runtime文件名 }), new BundleAnalyzerPlugin() // webpack打包結果分析 ] }) // css搖樹使用postcss插件實現了
babel.config.js咱們有一個兼容[vue-cli的瀏覽器兼容babel的配置]vue-cli
module.exports = { presets: [ '@vue/cli-plugin-babel/preset' ] }
最後貼的就是package.jsonnpm
{ "name": "vue-app-base", "version": "0.1.0", "private": true, "scripts": { "serve": "cross-env NODE_ENV=development webpack-dev-server --config webpack.dev.js", "build": "cross-env NODE_ENV=production webpack --config webpack.prod.js", "lint": "eslint --ext .js,.vue src/", "all-lint-fix": "eslint --fix --ext .js,.vue src/", "precommit": "lint-staged" }, "dependencies": { "core-js": "3", "vue": "^2.6.11" }, "devDependencies": { "@babel/core": "^7.12.9", "@babel/plugin-syntax-dynamic-import": "^7.8.3", "@babel/preset-env": "^7.12.7", "@fullhuman/postcss-purgecss": "^3.0.0", "@vue/cli-plugin-babel": "^4.5.9", "babel-loader": "^8.2.1", "clean-webpack-plugin": "^3.0.0", "copy-webpack-plugin": "^5.0.4", "cross-env": "^7.0.3", "css-loader": "^5.0.1", "cssnano": "^4.1.10", "eslint": "^7.14.0", "eslint-config-standard": "^16.0.2", "eslint-loader": "^4.0.2", "eslint-plugin-import": "^2.22.1", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-vue": "^7.1.0", "file-loader": "^6.2.0", "html-webpack-plugin": "^4.5.0", "husky": "^4.3.0", "image-webpack-loader": "^7.0.1", "less": "^3.12.2", "less-loader": "^7.1.0", "lint-staged": "^10.5.2", "mini-css-extract-plugin": "^1.3.1", "optimize-css-assets-webpack-plugin": "^5.0.4", "postcss": "^8.1.10", "postcss-cssnext": "^3.1.0", "postcss-import": "^13.0.0", "postcss-loader": "^4.1.0", "preload-webpack-plugin": "^3.0.0-beta.4", "script-ext-html-webpack-plugin": "^2.1.5", "style-loader": "^2.0.0", "terser-webpack-plugin": "2.2.1", "thread-loader": "^3.0.1", "url-loader": "^4.1.1", "vue-loader": "^15.9.5", "vue-template-compiler": "^2.6.12", "webpack": "^4.41.2", "webpack-bundle-analyzer": "^4.1.0", "webpack-cli": "3.3", "webpack-dev-server": "^3.9.0", "webpack-merge": "^5.4.0" }, "lint-staged": { "src/**/*.(js|vue)": [ "eslint --fix", "git add" ] }, "husky": { "hooks": { "pre-commit": "npm run precommit" } }, "eslintConfig": { "root": true, "env": { "node": true }, "extends": [ "plugin:vue/essential", "eslint:recommended" ], "parserOptions": { "parser": "babel-eslint" }, "rules": {} }, "browserslist": [ "> 1%", "last 2 versions", "IE 10" ], "sideEffects": [ "*.css", "*.less", "*.vue" ] }
這樣就配置完成了,這一套配置其實去除掉vue相關的就是一套通用配置。
作下webpack 的loader和plugin區別
webpack的執行流程咱們能夠直接來參考這張圖來看。