├── .eslintignore ├── src ├── style │ ├── reset.css │ └── base.scss ├── assets │ ├── logo.png │ └── man.jpg ├── App.vue └── index.js ├── .editorconfig ├── .eslintrc.js ├── .gitignore ├── webpack.analysis.config.js ├── .babelrc ├── commitlint.config.js ├── index.html ├── webpack.dll.config.js ├── webpack.prod.config.js ├── package.json ├── webpack.dev.config.js ├── webpack.base.config.js ├── vendor-manifest.json └── README.md /.eslintignore: -------------------------------------------------------------------------------- 1 | /dist/ 2 | /node_modules/ 3 | /*.js 4 | -------------------------------------------------------------------------------- /src/style/reset.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | } 5 | -------------------------------------------------------------------------------- /src/assets/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuoHui/webpack-demo/HEAD/src/assets/logo.png -------------------------------------------------------------------------------- /src/assets/man.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NuoHui/webpack-demo/HEAD/src/assets/man.jpg -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | indent_style = space 5 | indent_size = 2 6 | end_of_line = lf 7 | charset = utf-8 8 | trim_trailing_whitespace = true 9 | insert_final_newline = true 10 | -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: [ 3 | 'plugin:vue/essential' 4 | ], 5 | rules: { 6 | }, 7 | parserOptions: { 8 | parser: "babel-eslint", 9 | ecmaVersion: 2017 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | dist/ 7 | 8 | # Editor directories and files 9 | .idea 10 | .vscode 11 | *.suo 12 | *.ntvs* 13 | *.njsproj 14 | *.sln 15 | -------------------------------------------------------------------------------- /src/App.vue: -------------------------------------------------------------------------------- 1 | 6 | 11 | 17 | -------------------------------------------------------------------------------- /webpack.analysis.config.js: -------------------------------------------------------------------------------- 1 | const merge = require('webpack-merge') 2 | const prodWebpackConfig = require('./webpack.prod.config.js') 3 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 4 | 5 | module.exports = merge(prodWebpackConfig, { 6 | plugins: [ 7 | new BundleAnalyzerPlugin() // 打包分析 8 | ] 9 | }) 10 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import _ from 'lodash' 2 | import './style/reset.css' 3 | import './style/base.scss' 4 | 5 | import Vue from 'vue' 6 | import App from './App.vue' 7 | import ElementUI from 'element-ui' 8 | 9 | Vue.use(ElementUI) 10 | 11 | // eslint-disable-next-line 12 | new Vue({ 13 | el: '#app', 14 | render: h => h(App) 15 | }) 16 | window._ = _ 17 | -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | [ 4 | "@babel/preset-env" 5 | ] 6 | ], 7 | "plugins": [ 8 | [ 9 | "@babel/plugin-transform-runtime", 10 | { 11 | "corejs": false, 12 | "helpers": true, 13 | "regenerator": true, 14 | "useESModules": false, 15 | "absoluteRuntime": "@babel/runtime" 16 | } 17 | ] 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /src/style/base.scss: -------------------------------------------------------------------------------- 1 | $bd-bg: pink; 2 | body { 3 | background: $bd-bg; 4 | display: flex; 5 | .logo { 6 | background: url('../assets/logo.png') no-repeat; 7 | width: 100px; 8 | height: 100px; 9 | background-size: contain; 10 | } 11 | .man { 12 | background: url('../assets/man.jpg') no-repeat; 13 | width: 40px; 14 | height: 40px; 15 | background-size: contain; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /commitlint.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * 配置commit规则 3 | build:主要目的是修改项目构建系统(例如 glup,webpack,rollup 的配置等)的提交 4 | ci:主要目的是修改项目继续集成流程(例如 Travis,Jenkins,GitLab CI,Circle等)的提交 5 | docs:文档更新 6 | feat:新增功能 7 | merge:分支合并 Merge branch ? of ? 8 | fix:bug 修复 9 | perf:性能, 体验优化 10 | refactor:重构代码(既没有新增功能,也没有修复 bug) 11 | style:不影响程序逻辑的代码修改(修改空白字符,格式缩进,补全缺失的分号等,没有改变代码逻辑) 12 | test:新增测试用例或是更新现有测试 13 | revert:回滚某个更早之前的提交 14 | chore:不属于以上类型的其他类型 15 | */ 16 | module.exports = { 17 | extends: ['@commitlint/config-conventional'] 18 | } 19 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 14 | <%= htmlWebpackPlugin.options.title %> 15 | 16 | 17 | 18 |
19 | 20 |
21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /webpack.dll.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | const TerserPlugin = require('terser-webpack-plugin') 4 | 5 | module.exports = { 6 | mode: 'production', 7 | // 你想要打包的模块的数组 8 | entry: { 9 | vendor: ['vue/dist/vue.min.js', 'lodash', 'vuex', 'axios', 'vue-router', 'element-ui'] 10 | }, 11 | output: { 12 | path: path.join(__dirname, 'static/js'), // 打包后文件输出的位置 13 | filename: '[name].dll.js', 14 | library: '[name]_library' 15 | // vendor.dll.js中暴露出的全局变量名。 16 | // 主要是给DllPlugin中的name使用, 17 | // 故这里需要和webpack.DllPlugin中的`name: '[name]_library',`保持一致。 18 | }, 19 | plugins: [ 20 | new webpack.DllPlugin({ 21 | path: path.join(__dirname, '.', '[name]-manifest.json'), 22 | name: '[name]_library', 23 | context: __dirname 24 | }) 25 | ], 26 | optimization: { 27 | minimizer: [ 28 | // 压缩JS 29 | new TerserPlugin({ 30 | cache: true, 31 | parallel: true, 32 | sourceMap: false 33 | }) 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /webpack.prod.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 3 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin') 4 | const TerserPlugin = require('terser-webpack-plugin') 5 | const merge = require('webpack-merge') 6 | const baseWebpackConfig = require('./webpack.base.config.js') 7 | const devMode = process.env.NODE_ENV !== 'production' 8 | 9 | module.exports = merge(baseWebpackConfig, { 10 | mode: 'production', 11 | output: { 12 | filename: 'bound.[hash:5].js', 13 | path: path.resolve(__dirname, 'dist') 14 | }, 15 | module: { 16 | rules: [ 17 | // 处理css/scss/sass 18 | { 19 | test: /\.(sc|sa|c)ss$/, 20 | use: [ 21 | MiniCssExtractPlugin.loader, 22 | { 23 | loader: 'css-loader' 24 | }, 25 | { 26 | loader: 'postcss-loader', 27 | options: { 28 | ident: 'postcss', 29 | plugins: (loader) => [ 30 | require('autoprefixer')({ 31 | browsers: [ 32 | 'last 10 Chrome versions', 33 | 'last 5 Firefox versions', 34 | 'Safari >= 6', 35 | 'ie > 8' 36 | ] 37 | }) 38 | ] 39 | } 40 | }, 41 | { 42 | loader: 'sass-loader' 43 | } 44 | ] 45 | } 46 | ] 47 | }, 48 | plugins: [ 49 | // 提取CSS 50 | new MiniCssExtractPlugin({ 51 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名 52 | chunkFilename: devMode ? '[id].css' : '[id].[hash:5].css' 53 | }) 54 | ], 55 | optimization: { 56 | minimizer: [ 57 | // 压缩JS 58 | new TerserPlugin({ 59 | cache: true, 60 | parallel: true, 61 | sourceMap: true 62 | // 等等详细配置见官网 63 | }), 64 | // 压缩CSS 65 | new OptimizeCSSAssertsPlugin({}) 66 | ] 67 | } 68 | }) 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "webpack-demo", 3 | "version": "1.0.0", 4 | "description": "webpack4 demo", 5 | "main": "index.js", 6 | "dependencies": { 7 | "axios": "^0.18.0", 8 | "core-js": "^2.5.7", 9 | "element-ui": "^2.8.2", 10 | "vue": "^2.6.10", 11 | "vue-router": "^3.0.6", 12 | "vuex": "^3.1.1", 13 | "webpack": "^4.30.0" 14 | }, 15 | "devDependencies": { 16 | "@babel/core": "^7.4.4", 17 | "@babel/plugin-syntax-dynamic-import": "^7.2.0", 18 | "@babel/plugin-transform-runtime": "^7.4.4", 19 | "@babel/preset-env": "^7.4.4", 20 | "@babel/runtime": "^7.4.4", 21 | "@commitlint/cli": "^7.6.0", 22 | "@commitlint/config-conventional": "^7.6.0", 23 | "add-asset-html-webpack-plugin": "^3.1.3", 24 | "autoprefixer": "^9.5.1", 25 | "babel-eslint": "^10.0.1", 26 | "babel-loader": "^8.0.5", 27 | "clean-webpack-plugin": "^1.0.1", 28 | "cross-env": "^5.2.0", 29 | "css-loader": "^2.1.1", 30 | "eslint": "^5.16.0", 31 | "eslint-loader": "^2.1.2", 32 | "eslint-plugin-vue": "^5.2.2", 33 | "file-loader": "^3.0.1", 34 | "html-webpack-plugin": "^3.2.0", 35 | "husky": "^2.2.0", 36 | "image-webpack-loader": "^4.6.0", 37 | "lodash": "^4.17.11", 38 | "mini-css-extract-plugin": "^0.5.0", 39 | "node-sass": "^4.12.0", 40 | "optimize-css-assets-webpack-plugin": "^5.0.1", 41 | "postcss-import": "^12.0.1", 42 | "postcss-loader": "^3.0.0", 43 | "sass-loader": "^7.1.0", 44 | "standard": "^12.0.1", 45 | "style-loader": "^0.23.1", 46 | "terser-webpack-plugin": "^1.2.3", 47 | "uglifyjs-webpack-plugin": "^2.1.2", 48 | "url-loader": "^1.1.2", 49 | "vue-loader": "^15.7.0", 50 | "vue-style-loader": "^4.1.2", 51 | "vue-template-compiler": "^2.6.10", 52 | "webpack-bundle-analyzer": "^3.3.2", 53 | "webpack-cli": "^3.3.1", 54 | "webpack-dev-server": "^3.3.1", 55 | "webpack-merge": "^4.2.1" 56 | }, 57 | "scripts": { 58 | "dll": "webpack --config ./webpack.dll.config.js", 59 | "lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/", 60 | "dev": "cross-env NODE_ENV=development npx webpack-dev-server --open --progress --config webpack.dev.config.js", 61 | "build": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js", 62 | "build:report": "cross-env NODE_ENV=production npx webpack --progress --config webpack.analysis.config.js" 63 | }, 64 | "keywords": [ 65 | "webpack4", 66 | "demo" 67 | ], 68 | "husky": { 69 | "hooks": { 70 | "commit-msg": "commitlint -e $HUSKY_GIT_PARAMS", 71 | "pre-commit": "eslint \"src/**/*.{js,ts,vue}\"" 72 | } 73 | }, 74 | "author": "xyz", 75 | "license": "MIT" 76 | } 77 | -------------------------------------------------------------------------------- /webpack.dev.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const merge = require('webpack-merge') 3 | const baseWebpackConfig = require('./webpack.base.config.js') 4 | const webpack = require('webpack') 5 | const AddAssetHtmlPlugin = require('add-asset-html-webpack-plugin') 6 | const devConfigJs = { 7 | mode: 'development', 8 | output: { 9 | filename: 'bound.js', 10 | path: path.resolve(__dirname, 'dist') 11 | }, 12 | devtool: 'inline-source-map', 13 | module: { 14 | rules: [ 15 | // 处理css/scss/sass 16 | { 17 | test: /\.(sc|sa|c)ss$/, 18 | use: [ 19 | { 20 | loader: 'style-loader' 21 | }, 22 | { 23 | loader: 'css-loader', 24 | options: { 25 | sourceMap: true 26 | } 27 | }, 28 | { 29 | loader: 'postcss-loader', 30 | options: { 31 | ident: 'postcss', 32 | sourceMap: true, 33 | plugins: (loader) => [ 34 | require('autoprefixer')({ 35 | browsers: [ 36 | 'last 10 Chrome versions', 37 | 'last 5 Firefox versions', 38 | 'Safari >= 6', 39 | 'ie > 8' 40 | ] 41 | }) 42 | ] 43 | } 44 | }, 45 | { 46 | loader: 'sass-loader', 47 | options: { 48 | sourceMap: true 49 | } 50 | } 51 | ] 52 | } 53 | ] 54 | }, 55 | plugins: [ 56 | // 使用vendor-manifest.json引用dll 57 | new webpack.DllReferencePlugin({ 58 | context: __dirname, // context:与Dllplugin里的context所指向的上下文保持一致,这里都是指向了根目录 59 | manifest: require('./vendor-manifest.json') 60 | }), 61 | // 这个主要是将生成的vendor.dll.js文件加上hash值插入到页面中。 62 | new AddAssetHtmlPlugin([{ 63 | // dll文件位置 64 | filepath: path.resolve(__dirname, './static/js/vendor.dll.js'), 65 | // dll最终输出的目录 66 | outputPath: './js', 67 | // dll 引用路径 68 | publicPath: './js', 69 | includeSourcemap: false, 70 | hash: true 71 | }]), 72 | new webpack.NamedModulesPlugin(), // 更方便查看patch的依赖 73 | new webpack.HotModuleReplacementPlugin() // HMR 74 | ], 75 | devServer: { 76 | clientLogLevel: 'warning', // 输出日志级别 77 | hot: true, // 启用热更新 78 | contentBase: path.resolve(__dirname, 'dist'), // 告诉服务器从哪里提供内容 79 | publicPath: '/', // 此路径下的打包文件可在浏览器下访问 80 | compress: true, // 启用gzip压缩 81 | host: '0.0.0.0', 82 | port: 9991, 83 | open: true, // 自动打开浏览器 84 | overlay: { // 出现错误或者警告时候是否覆盖页面线上错误信息 85 | warnings: true, 86 | errors: true 87 | }, 88 | quiet: false, 89 | proxy: { // 设置代理 90 | '/dev': { 91 | target: 'http://dev.xxxx.com.cn', 92 | changeOrigin: true, 93 | pathRewrite: { 94 | '^/dev': '' 95 | } 96 | /** 97 | * 如果你的配置是 98 | * pathRewrite: { 99 | * '^/dev': '/order/api' 100 | * } 101 | * 即本地请求 /dev/getOrder => 实际上是 http://dev.xxxx.com.cn/order/api/getOrder 102 | */ 103 | }, 104 | '/test': { 105 | target: 'http://test.xxxx.com.cn', 106 | changeOrigin: true, 107 | pathRewrite: { 108 | '^/test': '' 109 | } 110 | }, 111 | '/prod': { 112 | target: 'http://prod.xxxx.com.cn', 113 | changeOrigin: true, 114 | pathRewrite: { 115 | '^/prod': '' 116 | } 117 | } 118 | }, 119 | watchOptions: { // 监控文件相关配置 120 | poll: true, 121 | ignored: /node_modules/, // 忽略监控的文件夹, 正则 122 | aggregateTimeout: 300 // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖) 123 | } 124 | } 125 | } 126 | 127 | module.exports = merge(baseWebpackConfig, devConfigJs) 128 | -------------------------------------------------------------------------------- /webpack.base.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const HtmlWebpackPlugin = require('html-webpack-plugin') 3 | const CleanWebpackplugin = require('clean-webpack-plugin') 4 | const VueLoaderPlugin = require('vue-loader/lib/plugin') 5 | 6 | module.exports = { 7 | entry: './src/index.js', 8 | resolve: { 9 | // 模块扩展名, 引入文件今可能写文件名 10 | extensions: ['.js', '.vue', '.json'], 11 | // 配置别名来映射路径 12 | alias: { 13 | '@': path.resolve(__dirname, 'src/'), 14 | // 对于固定的库, 减少递归解析 15 | 'vue': path.resolve(__dirname, 'node_modules/vue/dist/vue.min.js') 16 | }, 17 | // 指明第三方模块的绝对路径, 减少路径查找 18 | modules: [path.resolve(__dirname, 'node_modules')] 19 | }, 20 | // 配置外部依赖不会打包到boudle 21 | externals: { 22 | jquery: 'jQuery' 23 | }, 24 | module: { 25 | // 忽略对没采用模块化的模块进行递归解析 26 | noParse: [/vue\.min\.js/], 27 | rules: [ 28 | // 编译js 29 | { 30 | test: /\.js$/, 31 | exclude: /(node_modules|bower_components)/, 32 | include: path.resolve(__dirname, 'src'), // 缩小命中范围, 减少构建时间 33 | use: [ 34 | { 35 | loader: 'babel-loader?cacheDirectory' // 通过cacheDirectory选项开启支持缓存 36 | }, 37 | { 38 | loader: 'eslint-loader', 39 | options: { 40 | fix: true 41 | } 42 | } 43 | ] 44 | }, 45 | // 处理字体 46 | { 47 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 48 | loader: 'url-loader', 49 | options: { 50 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR 51 | limit: 10000, 52 | name: '[name]-[hash:5].[ext]', 53 | output: 'fonts/' 54 | // publicPath: '', 多用于CDN 55 | } 56 | }, 57 | // 处理文件 58 | { 59 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 60 | use: [ 61 | // 转base64 62 | { 63 | loader: 'url-loader', 64 | options: { 65 | // 具体配置见插件官网 66 | limit: 10000, 67 | name: '[name]-[hash:5].[ext]', 68 | outputPath: 'img/'// outputPath所设置的路径,是相对于 webpack 的输出目录。 69 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址 70 | } 71 | }, 72 | { 73 | loader: 'image-webpack-loader', 74 | options: { 75 | mozjpeg: { 76 | progressive: true, 77 | quality: 65 78 | }, 79 | // optipng.enabled: false will disable optipng 80 | optipng: { 81 | enabled: false 82 | }, 83 | pngquant: { 84 | quality: '65-90', 85 | speed: 4 86 | }, 87 | gifsicle: { 88 | interlaced: false 89 | }, 90 | // the webp option will enable WEBP 91 | webp: { 92 | quality: 75 93 | } 94 | } 95 | } 96 | ] 97 | }, 98 | { 99 | enforce: 'pre', 100 | test: /\.(js|vue)$/, 101 | loader: 'eslint-loader', 102 | exclude: /node_modules/ 103 | }, 104 | { 105 | test: /\.vue$/, 106 | use: [ 107 | { 108 | loader: 'vue-loader', 109 | options: { 110 | loaders: { 111 | sass: ['style-loader', 'css-loader', 'sass-loader', 'postcss-loader'] 112 | } 113 | } 114 | } 115 | ] 116 | } 117 | ] 118 | }, 119 | plugins: [ 120 | new CleanWebpackplugin(['dist']), 121 | // 打包模板 122 | new HtmlWebpackPlugin({ 123 | inject: true, 124 | hash: true, 125 | cache: true, 126 | chunksSortMode: 'none', 127 | title: 'Webapck4-demo', // 可以由外面传入 128 | filename: 'index.html', // 默认index.html 129 | template: path.resolve(__dirname, 'index.html'), 130 | minify: { 131 | collapseWhitespace: true, 132 | removeComments: true, 133 | removeRedundantAttributes: true, 134 | removeScriptTypeAttributes: true, 135 | removeStyleLinkTypeAttributes: true 136 | } 137 | }), 138 | new VueLoaderPlugin() 139 | ] 140 | } 141 | -------------------------------------------------------------------------------- /vendor-manifest.json: -------------------------------------------------------------------------------- 1 | {"name":"vendor_library","content":{"./node_modules/async-validator/es/util.js":{"id":0,"buildMeta":{"exportsType":"namespace","providedExports":["warning","format","isEmptyValue","isEmptyObject","asyncMap","complementError","deepMerge"]}},"./node_modules/async-validator/es/rule/index.js":{"id":1,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/axios/lib/utils.js":{"id":2,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/helpers/typeof.js":{"id":3,"buildMeta":{"providedExports":true}},"./node_modules/vue/dist/vue.runtime.esm.js":{"id":4,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/utils/util.js":{"id":5,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/dom.js":{"id":6,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_global.js":{"id":7,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/global.js":{"id":8,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_has.js":{"id":9,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/helpers/extends.js":{"id":10,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/emitter.js":{"id":11,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_hide.js":{"id":12,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-dp.js":{"id":13,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_descriptors.js":{"id":14,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-iobject.js":{"id":15,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks.js":{"id":16,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/messages.js":{"id":17,"buildMeta":{"exportsType":"namespace","providedExports":["newMessages","messages"]}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_core.js":{"id":18,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_is-object.js":{"id":19,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_fails.js":{"id":20,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/validator/index.js":{"id":21,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/locale/index.js":{"id":22,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_an-object.js":{"id":23,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_property-desc.js":{"id":24,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-keys.js":{"id":25,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_library.js":{"id":26,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_uid.js":{"id":27,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-pie.js":{"id":28,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/rule/required.js":{"id":29,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/type.js":{"id":30,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/axios/lib/defaults.js":{"id":31,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/vue-popper.js":{"id":32,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/merge.js":{"id":33,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/scrollbar-width.js":{"id":34,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/input.js":{"id":35,"buildMeta":{"providedExports":true}},"./node_modules/throttle-debounce/debounce.js":{"id":36,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/resize-event.js":{"id":37,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/focus.js":{"id":38,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_export.js":{"id":39,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-primitive.js":{"id":40,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_defined.js":{"id":41,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-integer.js":{"id":42,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_shared-key.js":{"id":43,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_shared.js":{"id":44,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_enum-bug-keys.js":{"id":45,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gops.js":{"id":46,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iterators.js":{"id":47,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_set-to-string-tag.js":{"id":48,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks-ext.js":{"id":49,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_wks-define.js":{"id":50,"buildMeta":{"providedExports":true}},"./node_modules/timers-browserify/main.js":{"id":51,"buildMeta":{"providedExports":true}},"./node_modules/process/browser.js":{"id":52,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/bind.js":{"id":53,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/adapters/xhr.js":{"id":54,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/createError.js":{"id":55,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/isCancel.js":{"id":56,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/Cancel.js":{"id":57,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/date.js":{"id":58,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/locale.js":{"id":59,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popup/index.js":{"id":60,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/mixins/migrating.js":{"id":61,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/clickoutside.js":{"id":62,"buildMeta":{"providedExports":true}},"./node_modules/throttle-debounce/throttle.js":{"id":63,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/scrollbar.js":{"id":64,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/tag.js":{"id":65,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/scroll-into-view.js":{"id":66,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/shared.js":{"id":67,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_ie8-dom-define.js":{"id":68,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_dom-create.js":{"id":69,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-keys-internal.js":{"id":70,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iobject.js":{"id":71,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_cof.js":{"id":72,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-object.js":{"id":73,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-define.js":{"id":74,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_redefine.js":{"id":75,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-create.js":{"id":76,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopn.js":{"id":77,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/validator/string.js":{"id":78,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/whitespace.js":{"id":79,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/type.js":{"id":80,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/range.js":{"id":81,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/enum.js":{"id":82,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/rule/pattern.js":{"id":83,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/method.js":{"id":84,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/number.js":{"id":85,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/boolean.js":{"id":86,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/regexp.js":{"id":87,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/integer.js":{"id":88,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/float.js":{"id":89,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/array.js":{"id":90,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/object.js":{"id":91,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/enum.js":{"id":92,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/pattern.js":{"id":93,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/date.js":{"id":94,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/async-validator/es/validator/required.js":{"id":95,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/vue/dist/vue.min.js":{"id":97,"buildMeta":{"providedExports":true}},"./node_modules/setimmediate/setImmediate.js":{"id":98,"buildMeta":{"providedExports":true}},"./node_modules/lodash/lodash.js":{"id":99,"buildMeta":{"providedExports":true}},"./node_modules/webpack/buildin/module.js":{"id":100,"buildMeta":{"providedExports":true}},"./node_modules/vuex/dist/vuex.esm.js":{"id":101,"buildMeta":{"exportsType":"namespace","providedExports":["default","Store","install","mapState","mapMutations","mapGetters","mapActions","createNamespacedHelpers"]}},"./node_modules/axios/index.js":{"id":102,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/axios.js":{"id":103,"buildMeta":{"providedExports":true}},"./node_modules/is-buffer/index.js":{"id":104,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/Axios.js":{"id":105,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/normalizeHeaderName.js":{"id":106,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/settle.js":{"id":107,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/enhanceError.js":{"id":108,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/buildURL.js":{"id":109,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/parseHeaders.js":{"id":110,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isURLSameOrigin.js":{"id":111,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/btoa.js":{"id":112,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/cookies.js":{"id":113,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/InterceptorManager.js":{"id":114,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/dispatchRequest.js":{"id":115,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/core/transformData.js":{"id":116,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/isAbsoluteURL.js":{"id":117,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/combineURLs.js":{"id":118,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/cancel/CancelToken.js":{"id":119,"buildMeta":{"providedExports":true}},"./node_modules/axios/lib/helpers/spread.js":{"id":120,"buildMeta":{"providedExports":true}},"./node_modules/vue-router/dist/vue-router.esm.js":{"id":121,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/element-ui.common.js":{"id":122,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/date-util.js":{"id":123,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/locale/lang/zh-CN.js":{"id":124,"buildMeta":{"providedExports":true}},"./node_modules/deepmerge/dist/cjs.js":{"id":125,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/locale/format.js":{"id":126,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popup/popup-manager.js":{"id":127,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/popper.js":{"id":128,"buildMeta":{"providedExports":true}},"./node_modules/resize-observer-polyfill/dist/ResizeObserver.es.js":{"id":129,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/element-ui/lib/button.js":{"id":130,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/checkbox.js":{"id":131,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/transitions/collapse-transition.js":{"id":132,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/vdom.js":{"id":133,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/tooltip.js":{"id":134,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/types.js":{"id":135,"buildMeta":{"providedExports":true}},"./node_modules/babel-helper-vue-jsx-merge-props/index.js":{"id":136,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/checkbox-group.js":{"id":137,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/after-leave.js":{"id":138,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/progress.js":{"id":139,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/select.js":{"id":140,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/option.js":{"id":141,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/button-group.js":{"id":142,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/index.js":{"id":143,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/normalizeWheel.js":{"id":144,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/UserAgent_DEPRECATED.js":{"id":145,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/isEventSupported.js":{"id":146,"buildMeta":{"providedExports":true}},"./node_modules/normalize-wheel/src/ExecutionEnvironment.js":{"id":147,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/aria-dialog.js":{"id":148,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/utils/aria-utils.js":{"id":149,"buildMeta":{"providedExports":true}},"./node_modules/async-validator/es/index.js":{"id":150,"buildMeta":{"exportsType":"namespace","providedExports":["default"]}},"./node_modules/babel-runtime/core-js/object/assign.js":{"id":151,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/object/assign.js":{"id":152,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.object.assign.js":{"id":153,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_ctx.js":{"id":154,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_a-function.js":{"id":155,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-assign.js":{"id":156,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_array-includes.js":{"id":157,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-length.js":{"id":158,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_to-absolute-index.js":{"id":159,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/core-js/symbol/iterator.js":{"id":160,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/symbol/iterator.js":{"id":161,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.string.iterator.js":{"id":162,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_string-at.js":{"id":163,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-create.js":{"id":164,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-dps.js":{"id":165,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_html.js":{"id":166,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gpo.js":{"id":167,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/web.dom.iterable.js":{"id":168,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.array.iterator.js":{"id":169,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_add-to-unscopables.js":{"id":170,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_iter-step.js":{"id":171,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/core-js/symbol.js":{"id":172,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/fn/symbol/index.js":{"id":173,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.symbol.js":{"id":174,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_meta.js":{"id":175,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_enum-keys.js":{"id":176,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_is-array.js":{"id":177,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopn-ext.js":{"id":178,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/_object-gopd.js":{"id":179,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es6.object.to-string.js":{"id":180,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es7.symbol.async-iterator.js":{"id":181,"buildMeta":{"providedExports":true}},"./node_modules/babel-runtime/node_modules/core-js/library/modules/es7.symbol.observable.js":{"id":182,"buildMeta":{"providedExports":true}},"./node_modules/element-ui/lib/input-number.js":{"id":183,"buildMeta":{"providedExports":true}}}} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # webpack-demo 2 | 3 | 玩转webpack4。 4 | 5 | 6 | # webpack4常规配置 7 | 8 | 总结Webpack4常见的配置, 一步步非常详细,不断更新。 9 | 10 | ## 准备开发环境 11 | 12 | ``` 13 | - 安装node 14 | - 安装webpack 15 | - npm init 初始化项目 16 | ``` 17 | 18 | ## 目录结构 19 | 20 | 21 | ![目录](https://user-gold-cdn.xitu.io/2019/1/26/16889053d7273593?w=610&h=432&f=png&s=44909) 22 | 23 | ## 写跑一个小demo 24 | 25 | ``` 26 | // src/index.js 27 | 28 | import _ from 'lodash' 29 | 30 | function create_div_element () { 31 | const div_element = document.createElement('div') 32 | div_element.innerHTML = _.join(['kobe', 'cpul'], ' ') 33 | return div_element 34 | } 35 | 36 | const div_ele = create_div_element() 37 | document.body.appendChild(div_ele) 38 | 39 | ``` 40 | 41 | ``` 42 | // dist/index.html 43 | 44 | 45 | 46 | 47 | 48 | webpack4 49 | 50 | 51 | 52 | 53 | 54 | 55 | ``` 56 | 57 | ``` 58 | // webpack.config.js 59 | 60 | const path = require('path') 61 | 62 | module.exports = { 63 | entry: './src/index.js', 64 | mode: 'development', 65 | output: { 66 | filename: 'bound.js', 67 | path: path.resolve(__dirname, 'dist') 68 | } 69 | } 70 | 71 | ``` 72 | 73 | 然后通过[npx](https://github.com/zkat/npx#readme)执行webpack进行打包。 74 | 75 | 或者配成一个script命令也可以。 76 | 77 | ``` 78 | "scripts": { 79 | "build": "npx webpack -c webpack.config.js" 80 | } 81 | ``` 82 | 83 | ``` 84 | npx webpack 85 | ``` 86 | 87 | 在浏览器打开index.html就会发现代码执行成功了。 88 | 89 | 90 | ## webpack处理CSS 91 | 92 | 假设我们现在需要在index.js引入css文件。 93 | 94 | ``` 95 | // index.js 96 | 97 | import './style/reset.css' 98 | ``` 99 | 100 | 101 | 我们需要使用专门的loader来解析css, 并把css注入到html文件 102 | ``` 103 | npm i -D css-loader style-loader 104 | ``` 105 | 106 | 修改webpack配置文件 107 | ``` 108 | const path = require('path') 109 | 110 | module.exports = { 111 | entry: './src/index.js', 112 | mode: 'development', 113 | output: { 114 | filename: 'bound.js', 115 | path: path.resolve(__dirname, 'dist') 116 | }, 117 | module: { 118 | rules: [ 119 | { 120 | test: /\.css/, 121 | use: ['style-loader', 'css-loader'] // use的顺序从右往左 122 | } 123 | ] 124 | } 125 | } 126 | ``` 127 | 这个时候你在npx webpack, 打包后执行index.html你会发现css已经注入成功了。 128 | 129 | ## webpack处理sass文件 130 | 131 | 现在前端项目都是使用一些css预处理器来帮助更好的使用CSS,如Sass等。 132 | 133 | 假设我们现在index.js中需要引入一个base.scss文件。 134 | 那么webpack改如何处理sass/scss文件呢? 135 | 136 | ``` 137 | npm install sass-loader node-sass -D 138 | ``` 139 | 140 | ``` 141 | // src/style/base.scss 142 | 143 | $bd-bg: pink; 144 | body { 145 | background: $bd-bg; 146 | } 147 | ``` 148 | 149 | ``` 150 | // index.js 151 | 152 | import './style/base.scss' 153 | ``` 154 | 155 | 更过配置文件处理scss 156 | ``` 157 | const path = require('path') 158 | 159 | module.exports = { 160 | entry: './src/index.js', 161 | mode: 'development', 162 | output: { 163 | filename: 'bound.js', 164 | path: path.resolve(__dirname, 'dist') 165 | }, 166 | module: { 167 | rules: [ 168 | { 169 | test: /\.(sc|sa|c)ss$/, 170 | use: ['style-loader', 'css-loader', 'sass-loader'] // use的顺序从右往左 171 | } 172 | ] 173 | } 174 | } 175 | 176 | ``` 177 | 178 | ## webpack为sass添加source map 179 | 180 | 配置source map是为了当出现错误时候方便我们进行定位调试, 当然我们在生产环境不需要启动这个。 181 | 182 | 像我们上面例子中, 你会发现打包后我们看不出scss来自哪个文件。 183 | 184 | 185 | ![noe-source-map](https://user-gold-cdn.xitu.io/2019/1/26/168899427382104f?w=2548&h=624&f=png&s=224107) 186 | 187 | 修改webpack配置文件。 188 | 189 | ``` 190 | const path = require('path') 191 | 192 | module.exports = { 193 | entry: './src/index.js', 194 | mode: 'development', 195 | output: { 196 | filename: 'bound.js', 197 | path: path.resolve(__dirname, 'dist') 198 | }, 199 | module: { 200 | rules: [ 201 | { 202 | test: /\.(sc|sa|c)ss$/, 203 | use: [ 204 | { 205 | loader: 'style-loader' 206 | }, 207 | { 208 | loader: 'css-loader', 209 | options: { 210 | sourceMap: true 211 | } 212 | }, 213 | { 214 | loader: 'sass-loader', 215 | options: { 216 | sourceMap: true 217 | } 218 | } 219 | ] 220 | } 221 | ] 222 | } 223 | } 224 | 225 | ``` 226 | 227 | 打包后在浏览器打开index.html. 228 | 229 | 230 | ![sourcemap](https://user-gold-cdn.xitu.io/2019/1/26/16889987ec88f4b3?w=5116&h=760&f=jpeg&s=311054) 231 | 232 | ## webpack为css添加CSS3前缀 233 | 234 | [PostCSS](https://www.postcss.com.cn/)是一个用 JavaScript 工具和插件转换 CSS 代码的工具, 功能强大, 我们最常用的就是利用PostCSS帮我们Autoprefixer 自动获取浏览器的流行度和能够支持的属性,并根据这些数据帮你自动为 CSS 规则添加前缀。 235 | 236 | 237 | ``` 238 | npm i -D postcss-loader autoprefixer postcss-import 239 | 240 | // postcss-import: 在使用@import css文件时候让webpack可以监听并编译 241 | // postcss-nextcss: 支持css4 242 | ``` 243 | 244 | 修改配置文件 245 | 246 | ``` 247 | rules: [ 248 | { 249 | test: /\.(sc|sa|c)ss$/, 250 | use: [ 251 | { 252 | loader: 'style-loader' 253 | }, 254 | { 255 | loader: 'css-loader', 256 | options: { 257 | sourceMap: true 258 | } 259 | }, 260 | { 261 | loader: 'postcss-loader', 262 | options: { 263 | ident: 'postcss', 264 | sourceMap: true, 265 | plugins: loader => [ 266 | // 可以配置多个插件 267 | require('autoprefixer')({ 268 | browsers: [' > 0.15% in CN '] 269 | }) 270 | ] 271 | } 272 | }, 273 | { 274 | loader: 'sass-loader', 275 | options: { 276 | sourceMap: true 277 | } 278 | } 279 | ] 280 | } 281 | ] 282 | ``` 283 | 284 | 285 | ![css3前缀](https://user-gold-cdn.xitu.io/2019/1/26/16889ac8a2366de2?w=2186&h=494&f=png&s=157203) 286 | 287 | ## 抽离样式表为单独的css文件并打版本号 288 | 289 | 抽离css前提是我们只在生产环境这么做, 因此你的配置文件的mode: production。 290 | 291 | 另外抽离了css就不能在使用style-loader注入到html文件。 292 | 293 | ``` 294 | npm i -D mini-css-extract-plugin 295 | ``` 296 | 297 | 配置一个script命名 298 | 299 | ``` 300 | "scripts": { 301 | "dist": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js" 302 | }, 303 | ``` 304 | 305 | 添加一个webpack.prod.config.js.当然正式项目我们是会拆分配置文件, 然后通过merge处理。 306 | 307 | ``` 308 | - webpack.base.config.js 309 | - webpack.dev.config.js 310 | - webpack.prod.config.js 311 | - webpack.vue.config.js 312 | ``` 313 | 314 | 这里demo就没有这么做, 所以代码有些冗余。 315 | 316 | ``` 317 | // webpack.prod.config.js 318 | 319 | const path = require('path') 320 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 321 | const devMode = process.env.NODE_ENV !== 'production' 322 | 323 | module.exports = { 324 | entry: './src/index.js', 325 | mode: 'production', 326 | output: { 327 | filename: 'bound.js', 328 | path: path.resolve(__dirname, 'dist') 329 | }, 330 | module: { 331 | rules: [ 332 | { 333 | test: /\.(sc|sa|c)ss$/, 334 | use: [ 335 | MiniCssExtractPlugin.loader, 336 | { 337 | loader: 'css-loader' 338 | }, 339 | { 340 | loader: 'postcss-loader', 341 | options: { 342 | ident: 'postcss', 343 | plugins: (loader) => [ 344 | require('autoprefixer')({ 345 | browsers: [ 346 | 'last 10 Chrome versions', 347 | 'last 5 Firefox versions', 348 | 'Safari >= 6', 349 | 'ie > 8' 350 | ] 351 | }) 352 | ] 353 | } 354 | }, 355 | { 356 | loader: 'sass-loader' 357 | } 358 | ] 359 | } 360 | ] 361 | }, 362 | plugins: [ 363 | new MiniCssExtractPlugin({ 364 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名 365 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css' 366 | }) 367 | ] 368 | } 369 | 370 | ``` 371 | 372 | 打包后你会发现 373 | 374 | 375 | ![main.css](https://user-gold-cdn.xitu.io/2019/1/26/16889d416b922ff4?w=582&h=482&f=png&s=53322) 376 | 377 | 这个时候我们如果去使用只能在index.html去引用它了, 很明显这是不方便的, 因为我们css文件肯定很庞大, 后面会解决这个问题, 这里就略过。 378 | 379 | 380 | ## webpack压缩JS和CSS 381 | 382 | 压缩的作用自然是为了减小包的体积了, 提升加载效率, 因此压缩都是配置在生产环境。 383 | 384 | ### 压缩css 385 | 386 | Webpack后面版本应该会内置CSS压缩, 目前先手工配置。 387 | 388 | ``` 389 | npm i -D optimize-css-assets-webpack-plugin 390 | ``` 391 | 392 | 更改配置文件: 393 | 394 | ``` 395 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin') 396 | 397 | optimization: { 398 | minimizer: [ 399 | // 压缩CSS 400 | new OptimizeCSSAssertsPlugin({}) 401 | ] 402 | } 403 | ``` 404 | 405 | 406 | ![压缩css](https://user-gold-cdn.xitu.io/2019/1/26/16889e0ed14dec47?w=2282&h=730&f=png&s=151485) 407 | 408 | ### JS压缩 409 | 410 | 411 | ``` 412 | npm i -D uglifyjs-webpack-plugin 413 | ``` 414 | 415 | 修改配置文件 416 | 417 | ``` 418 | const UglifyJsPlugin = require('uglifyjs-webpack-plugin') 419 | 420 | optimization: { 421 | minimizer: [ 422 | // 压缩JS 423 | new UglifyJsPlugin({ 424 | // 有很多可以配置 425 | cache: true, 426 | parallel: true, 427 | sourceMap: true, 428 | uglifyOptions: { 429 | // 在UglifyJs删除没有用到的代码时不输出警告 430 | warnings: false, 431 | output: { 432 | // 删除所有的注释 433 | comments: false, 434 | // 最紧凑的输出 435 | beautify: false 436 | }, 437 | compress: { 438 | // 删除所有的 `console` 语句 439 | // 还可以兼容ie浏览器 440 | drop_console: true, 441 | // 内嵌定义了但是只用到一次的变量 442 | collapse_vars: true, 443 | // 提取出出现多次但是没有定义成变量去引用的静态值 444 | reduce_vars: true, 445 | } 446 | } 447 | }) 448 | ] 449 | } 450 | ``` 451 | 452 | 453 | 454 | 这个时候去打包我发现一个错误, [ERROR in js/background.js from UglifyJs Unexpected token: keyword (const)](https://github.com/webpack-contrib/uglifyjs-webpack-plugin/issues/389)。 455 | 456 | Uglify-js不支持es6语法,请使用terser插件, 于是我们更改使用terser插件试试, 其实你继续用uglifyjs-webpack-plugin也可以, 只需要配合babel先转下。 457 | 458 | ``` 459 | npm install terser-webpack-plugin -D 460 | ``` 461 | 462 | 463 | 更多使用见官网[terser-webpack-plugin](https://www.npmjs.com/package/terser-webpack-plugin)。 464 | 465 | ``` 466 | 467 | optimization: { 468 | minimizer: [ 469 | // 压缩JS 470 | new TerserPlugin({ 471 | cache: true, 472 | parallel: true, 473 | sourceMap: true, 474 | // 等等详细配置见官网 475 | }), 476 | 477 | ] 478 | } 479 | ``` 480 | 481 | 482 | ![压缩js](https://user-gold-cdn.xitu.io/2019/1/26/16889f7537a332ee?w=2188&h=734&f=png&s=148909) 483 | 484 | ## webpack处理带哈希值的文件名引入问题 485 | 486 | 我们给打包的文件打上hash是为了解决缓存更新问题,常见需要打上hash的地方有。 487 | 488 | ``` 489 | output: { 490 | filename: 'bound.[hash:5].js', 491 | path: path.resolve(__dirname, 'dist') 492 | } 493 | ``` 494 | 495 | ``` 496 | // 提取CSS 497 | new MiniCssExtractPlugin({ 498 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名 499 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css' 500 | }) 501 | ``` 502 | 503 | 但是打上hash我们怎么引入是一个问题。 504 | 505 | html-webpack-plugin插件可以把js/css注入到一个模板文件, 所以不需要再手动更改引用。 506 | 507 | ``` 508 | npm i -D html-webpack-plugin 509 | ``` 510 | 511 | 更改配置文件 512 | 513 | ``` 514 | const HtmlWebpackPlugin = require('html-webpack-plugin') 515 | 516 | plugins: [ 517 | // 打包模板 518 | new HtmlWebpackPlugin({ 519 | inject: true, 520 | hash: true, 521 | cache: true, 522 | chunksSortMode: 'none', 523 | title: 'Webapck4-demo', // 可以由外面传入 524 | filename: 'index.html', // 默认index.html 525 | template: path.resolve(__dirname, 'index.html'), 526 | minify: { 527 | collapseWhitespace: true, 528 | removeComments: true, 529 | removeRedundantAttributes: true, 530 | removeScriptTypeAttributes: true, 531 | removeStyleLinkTypeAttributes: true 532 | } 533 | }) 534 | ], 535 | ``` 536 | 537 | 设置一个模板文件。 538 | 539 | ``` 540 | // index.html 541 | 542 | 543 | 544 | 545 | 546 | 547 | 548 | 549 | 550 | <%= htmlWebpackPlugin.options.title %> 551 | 552 | 553 | 554 |
555 | 556 | 557 | 558 | ``` 559 | 打包后的文件: 560 | 561 | ![打包后文件](https://user-gold-cdn.xitu.io/2019/1/26/1688a3ed90238b7d?w=2172&h=1608&f=jpeg&s=198912) 562 | 563 | ``` 564 | 565 | 566 | 567 | 568 | 569 | 570 | 571 | 572 | 573 | 574 | 575 | 576 | 577 | 578 |
579 | 580 | 581 | 582 | 583 | ``` 584 | 585 | ## webpack清理打包后的dist目录 586 | 587 | 我们会发现每次打包后dist文件夹都会不断增加文件, 显然这个方面我们需要处理, 但是某些情况下我们不需要去清理, 比如坑爹的微信公众号缓存问题。 588 | 589 | ``` 590 | npm i -D clean-webpack-plugin 591 | ``` 592 | 593 | 修改配置文件 594 | 595 | ``` 596 | const CleanWebpackplugin = require('clean-webpack-plugin') 597 | 598 | plugins: [ 599 | // 清理dist目录 600 | new CleanWebpackplugin(['dist']) 601 | ] 602 | ``` 603 | 604 | ## webpack处理图片以及优化 605 | 606 | 我们这里只是为了测试, 在index.html模板文件添加一个dom去使用图片。 607 | 608 | ``` 609 | // index.html 610 | 611 | 612 | // base.scss 613 | .logo { 614 | background: url('../assets/logo.png') no-repeat; 615 | width: 100px; 616 | height: 100px; 617 | background-size: contain; 618 | } 619 | ``` 620 | 621 | 使用file-loader来处理文件的导入 622 | 623 | ``` 624 | npm i -D file-loader 625 | ``` 626 | 627 | 修改配置文件 628 | 629 | ``` 630 | rules: [ 631 | // 处理文件 632 | { 633 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 634 | use: [ 635 | { 636 | loader: 'file-loader', 637 | options: { 638 | // 具体配置见插件官网 639 | limit: 1, 640 | name: '[name]-[hash:5].[ext]', 641 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。 642 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址 643 | }, 644 | }, 645 | ] 646 | }, 647 | ] 648 | ``` 649 | 650 | 下面继续对图片进行优化和压缩 651 | 652 | ``` 653 | npm i -D image-webpack-loader 654 | ``` 655 | 656 | 修改配置文件 657 | 658 | ``` 659 | // 处理文件 660 | { 661 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 662 | use: [ 663 | { 664 | loader: 'file-loader', 665 | options: { 666 | // 具体配置见插件官网 667 | limit: 10000, 668 | name: '[name]-[hash:5].[ext]', 669 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。 670 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址 671 | }, 672 | }, 673 | { 674 | loader: 'image-webpack-loader', 675 | options: { 676 | mozjpeg: { 677 | progressive: true, 678 | quality: 65 679 | }, 680 | // optipng.enabled: false will disable optipng 681 | optipng: { 682 | enabled: false, 683 | }, 684 | pngquant: { 685 | quality: '65-90', 686 | speed: 4 687 | }, 688 | gifsicle: { 689 | interlaced: false, 690 | }, 691 | // the webp option will enable WEBP 692 | webp: { 693 | quality: 75 694 | } 695 | } 696 | } 697 | ] 698 | }, 699 | ``` 700 | 701 | 压缩前图片大小181.46kb. 702 | 703 | ![未压缩](https://user-gold-cdn.xitu.io/2019/1/26/1688a6b1b5b5911c?w=2464&h=1352&f=png&s=379430) 704 | 705 | 压缩后29kb. 706 | 707 | ![压缩图片后](https://user-gold-cdn.xitu.io/2019/1/26/1688a6c9d26a2def?w=2456&h=1470&f=png&s=657561) 708 | 709 | 710 | ## webpack把图片转为base64以及字体处理 711 | 712 | 通过把一些小的图片转为base65(DataURl)可以减少http请求, 提升访问效率。 713 | 714 | ``` 715 | npm i -D url-loader 716 | ``` 717 | 718 | 修改配置文件 719 | 720 | ``` 721 | // 处理文件 722 | { 723 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 724 | use: [ 725 | { 726 | loader: 'url-loader', 727 | options: { 728 | // 具体配置见插件官网 729 | limit: 10000, 730 | name: '[name]-[hash:5].[ext]', 731 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。 732 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址 733 | }, 734 | }, 735 | { 736 | loader: 'image-webpack-loader', 737 | options: { 738 | mozjpeg: { 739 | progressive: true, 740 | quality: 65 741 | }, 742 | // optipng.enabled: false will disable optipng 743 | optipng: { 744 | enabled: false, 745 | }, 746 | pngquant: { 747 | quality: '65-90', 748 | speed: 4 749 | }, 750 | gifsicle: { 751 | interlaced: false, 752 | }, 753 | // the webp option will enable WEBP 754 | webp: { 755 | quality: 75 756 | } 757 | } 758 | } 759 | ] 760 | }, 761 | ``` 762 | 这里测试的话我们需要准备一个小的图片即可,如上述配置所述只要小于10kb就会用base64替代。 763 | 764 | 765 | ![转base64](https://user-gold-cdn.xitu.io/2019/1/26/1688a7bc21b4b3a8?w=5096&h=1120&f=jpeg&s=476780). 766 | 767 | 字体处理的话配置如下: 768 | 769 | ``` 770 | { 771 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 772 | loader: 'url-loader', 773 | options: { 774 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR 775 | limit: 10000, 776 | name: '[name]-[hash:5].[ext]', 777 | output: 'fonts/', 778 | // publicPath: '', 多用于CDN 779 | } 780 | }, 781 | ``` 782 | 783 | 784 | 785 | ## webpack配置分层 786 | 787 | 之前有提过webpack根据不同的环境我们会加载不同的配置。我们只需要提取出三部分。 788 | 789 | ``` 790 | - base: 公共的部分 791 | - dev: 开发环境部分 792 | - prod: 生产环境部分 793 | ``` 794 | 795 | ``` 796 | npm i -D webpack-merge 797 | ``` 798 | 799 | 我们这里现在简单分层:正式项目最好创建一个config/webpack目录管理。 800 | 801 | 802 | ![webpack目录](https://user-gold-cdn.xitu.io/2019/1/26/1688a8b03626dd51?w=1156&h=968&f=jpeg&s=89511) 803 | 804 | 下面是源代码。 805 | 806 | ``` 807 | "scripts": { 808 | "dev": "cross-env NODE_ENV=development npx webpack --progress --config webpack.dev.config.js", 809 | "build": "cross-env NODE_ENV=production npx webpack --progress --config webpack.prod.config.js" 810 | }, 811 | ``` 812 | 813 | ``` 814 | // webapck.base.config.js 815 | 816 | const path = require('path') 817 | const HtmlWebpackPlugin = require('html-webpack-plugin') 818 | const CleanWebpackplugin = require('clean-webpack-plugin') 819 | 820 | module.exports = { 821 | entry: './src/index.js', 822 | module: { 823 | rules: [ 824 | // 处理字体 825 | { 826 | test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, 827 | loader: 'url-loader', 828 | options: { 829 | // 文件大小小于limit参数,url-loader将会把文件转为DataUR 830 | limit: 10000, 831 | name: '[name]-[hash:5].[ext]', 832 | output: 'fonts/', 833 | // publicPath: '', 多用于CDN 834 | } 835 | }, 836 | // 处理文件 837 | { 838 | test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, 839 | use: [ 840 | // 转base64 841 | { 842 | loader: 'url-loader', 843 | options: { 844 | // 具体配置见插件官网 845 | limit: 10000, 846 | name: '[name]-[hash:5].[ext]', 847 | outputPath: 'img/', // outputPath所设置的路径,是相对于 webpack 的输出目录。 848 | // publicPath 选项则被许多webpack的插件用于在生产模式下更新内嵌到css、html文件内的 url , 如CDN地址 849 | }, 850 | }, 851 | { 852 | loader: 'image-webpack-loader', 853 | options: { 854 | mozjpeg: { 855 | progressive: true, 856 | quality: 65 857 | }, 858 | // optipng.enabled: false will disable optipng 859 | optipng: { 860 | enabled: false, 861 | }, 862 | pngquant: { 863 | quality: '65-90', 864 | speed: 4 865 | }, 866 | gifsicle: { 867 | interlaced: false, 868 | }, 869 | // the webp option will enable WEBP 870 | webp: { 871 | quality: 75 872 | } 873 | } 874 | } 875 | ] 876 | } 877 | ] 878 | }, 879 | plugins: [ 880 | // 打包模板 881 | new HtmlWebpackPlugin({ 882 | inject: true, 883 | hash: true, 884 | cache: true, 885 | chunksSortMode: 'none', 886 | title: 'Webapck4-demo', // 可以由外面传入 887 | filename: 'index.html', // 默认index.html 888 | template: path.resolve(__dirname, 'index.html'), 889 | minify: { 890 | collapseWhitespace: true, 891 | removeComments: true, 892 | removeRedundantAttributes: true, 893 | removeScriptTypeAttributes: true, 894 | removeStyleLinkTypeAttributes: true 895 | } 896 | }), 897 | // 清理dist目录 898 | new CleanWebpackplugin(['dist']) 899 | ] 900 | } 901 | 902 | ``` 903 | 904 | 905 | ``` 906 | // webpack.dev.config.js 907 | 908 | const path = require('path') 909 | const merge = require('webpack-merge') 910 | const baseWebpackConfig = require('./webpack.base.config.js') 911 | 912 | 913 | module.exports = merge(baseWebpackConfig, { 914 | mode: 'development', 915 | output: { 916 | filename: 'bound.js', 917 | path: path.resolve(__dirname, 'dist') 918 | }, 919 | module: { 920 | rules: [ 921 | // 处理css/scss/sass 922 | { 923 | test: /\.(sc|sa|c)ss$/, 924 | use: [ 925 | { 926 | loader: 'style-loader', 927 | }, 928 | { 929 | loader: 'css-loader', 930 | options: { 931 | sourceMap: true 932 | } 933 | }, 934 | { 935 | loader: 'postcss-loader', 936 | options: { 937 | ident: 'postcss', 938 | sourceMap: true, 939 | plugins: (loader) => [ 940 | require('autoprefixer')({ 941 | browsers: [ 942 | 'last 10 Chrome versions', 943 | 'last 5 Firefox versions', 944 | 'Safari >= 6', 945 | 'ie > 8' 946 | ] 947 | }) 948 | ] 949 | } 950 | }, 951 | { 952 | loader: 'sass-loader', 953 | options: { 954 | sourceMap: true 955 | } 956 | } 957 | ] 958 | } 959 | ] 960 | } 961 | }) 962 | 963 | ``` 964 | 965 | 966 | ``` 967 | // webapck.prod.config.js 968 | 969 | const path = require('path') 970 | const MiniCssExtractPlugin = require('mini-css-extract-plugin') 971 | const OptimizeCSSAssertsPlugin = require('optimize-css-assets-webpack-plugin') 972 | const TerserPlugin = require('terser-webpack-plugin') 973 | const merge = require('webpack-merge') 974 | const baseWebpackConfig = require('./webpack.base.config.js') 975 | const devMode = process.env.NODE_ENV !== 'production' 976 | 977 | module.exports = merge(baseWebpackConfig, { 978 | mode: 'production', 979 | output: { 980 | filename: 'bound.[hash:5].js', 981 | path: path.resolve(__dirname, 'dist') 982 | }, 983 | module: { 984 | rules: [ 985 | // 处理css/scss/sass 986 | { 987 | test: /\.(sc|sa|c)ss$/, 988 | use: [ 989 | MiniCssExtractPlugin.loader, 990 | { 991 | loader: 'css-loader' 992 | }, 993 | { 994 | loader: 'postcss-loader', 995 | options: { 996 | ident: 'postcss', 997 | plugins: (loader) => [ 998 | require('autoprefixer')({ 999 | browsers: [ 1000 | 'last 10 Chrome versions', 1001 | 'last 5 Firefox versions', 1002 | 'Safari >= 6', 1003 | 'ie > 8' 1004 | ] 1005 | }) 1006 | ] 1007 | } 1008 | }, 1009 | { 1010 | loader: 'sass-loader' 1011 | } 1012 | ] 1013 | } 1014 | ] 1015 | }, 1016 | plugins: [ 1017 | // 提取CSS 1018 | new MiniCssExtractPlugin({ 1019 | filename: devMode ? '[name].css' : '[name].[hash:5].css', // 设置输出的文件名 1020 | chunkFilename: devMode ? '[id].css': '[id].[hash:5].css' 1021 | }) 1022 | ], 1023 | optimization: { 1024 | minimizer: [ 1025 | // 压缩JS 1026 | new TerserPlugin({ 1027 | cache: true, 1028 | parallel: true, 1029 | sourceMap: true, 1030 | // 等等详细配置见官网 1031 | }), 1032 | // 压缩CSS 1033 | new OptimizeCSSAssertsPlugin({}) 1034 | ] 1035 | } 1036 | }) 1037 | 1038 | ``` 1039 | 1040 | ## webpack配置js使用sourceMap 1041 | 1042 | 在webpack4使用inline-source-map选项就可以启动错误的堆栈跟踪, 只用于开发环境 1043 | 1044 | ``` 1045 | devtool: 'inline-source-map' 1046 | ``` 1047 | 1048 | ## 监控文件变化自动编译 1049 | 1050 | 1051 | 简单的方法就是启动watch模式: 如 1052 | 1053 | ``` 1054 | "dev": "cross-env NODE_ENV=development npx webpack --progress --config webpack.dev.config.js --watch" 1055 | ``` 1056 | 1057 | ## webpack开启热更新和代理配置 1058 | 1059 | 很明显上面watch模式效率不高而且很不方便, 编译完还需要刷新页面, webpack可以开启热更新模式,大大加速开大效率。 1060 | 1061 | ``` 1062 | npm i -D webpack-dev-server 1063 | ``` 1064 | 1065 | 修改script脚本。 1066 | 1067 | ``` 1068 | "dev": "cross-env NODE_ENV=development npx webpack-dev-server --progress --config webpack.dev.config.js" 1069 | ``` 1070 | 1071 | 修改配置文件 1072 | 1073 | ``` 1074 | const webpack = require('webpack') 1075 | 1076 | 1077 | plugins: [ 1078 | new webpack.NamedModulesPlugin(), // 更方便查看patch的依赖 1079 | new webpack.HotModuleReplacementPlugin() // HMR 1080 | ], 1081 | devServer: { 1082 | clientLogLevel: 'warning', // 输出日志级别 1083 | hot: true, // 启用热更新 1084 | contentBase: path.resolve(__dirname, 'dist'), // 告诉服务器从哪里提供内容 1085 | publicPath: '/', // 此路径下的打包文件可在浏览器下访问 1086 | compress: true, // 启用gzip压缩 1087 | // publicPath: './', 1088 | disableHostCheck: true, 1089 | host: '0.0.0.0', 1090 | port: 9999, 1091 | open: true, // 自动打开浏览器 1092 | overlay: { // 出现错误或者警告时候是否覆盖页面线上错误信息 1093 | warnings: true, 1094 | errors: true 1095 | }, 1096 | quiet: true, 1097 | proxy: { // 设置代理 1098 | '/dev': { 1099 | target: 'http://dev.xxxx.com.cn', 1100 | changeOrigin: true, 1101 | pathRewrite: { 1102 | '^/dev': '' 1103 | } 1104 | /** 1105 | * 如果你的配置是 1106 | * pathRewrite: { 1107 | '^/dev': '/order/api' 1108 | } 1109 | 即本地请求 /dev/getOrder => 实际上是 http://dev.xxxx.com.cn/order/api/getOrder 1110 | */ 1111 | }, 1112 | '/test': { 1113 | target: 'http://test.xxxx.com.cn', 1114 | changeOrigin: true, 1115 | pathRewrite: { 1116 | '^/test': '' 1117 | } 1118 | }, 1119 | '/prod': { 1120 | target: 'http://prod.xxxx.com.cn', 1121 | changeOrigin: true, 1122 | pathRewrite: { 1123 | '^/prod': '' 1124 | } 1125 | } 1126 | }, 1127 | watchOptions: { // 监控文件相关配置 1128 | poll: true, 1129 | ignored: /node_modules/, // 忽略监控的文件夹, 正则 1130 | aggregateTimeout: 300, // 默认值, 当你连续改动时候, webpack可以设置构建延迟时间(防抖) 1131 | } 1132 | } 1133 | ``` 1134 | 1135 | 1136 | ## webpack启用babel转码 1137 | 1138 | ``` 1139 | npm i -D babel-loader @babel/core @babel/preset-env @babel/runtime @babel/plugin-transform-runtime 1140 | ``` 1141 | 1142 | 修改配置文件 1143 | 1144 | ``` 1145 | // 编译js 1146 | { 1147 | test: /\.js$/, 1148 | exclude: /(node_modules|bower_components)/, 1149 | use: { 1150 | loader: 'babel-loader?cacheDirectory', // 通过cacheDirectory选项开启支持缓存 1151 | options: { 1152 | presets: ['@babel/preset-env'] 1153 | } 1154 | } 1155 | }, 1156 | ``` 1157 | 1158 | 增加.babelrc配置文件 1159 | 1160 | 1161 | ``` 1162 | // 仅供参考 1163 | 1164 | { 1165 | "presets": [ 1166 | [ 1167 | "@babel/preset-env" 1168 | ] 1169 | ], 1170 | "plugins": [ 1171 | [ 1172 | "@babel/plugin-transform-runtime", 1173 | { 1174 | "corejs": false, 1175 | "helpers": true, 1176 | "regenerator": true, 1177 | "useESModules": false, 1178 | "absoluteRuntime": "@babel/runtime" 1179 | } 1180 | ] 1181 | ] 1182 | } 1183 | 1184 | ``` 1185 | 1186 | ## webpack配置eslint校验 1187 | 1188 | ``` 1189 | npm i -D eslint eslint-loader 1190 | 1191 | // 校验规则 1192 | npm i -D babel-eslint standard 1193 | ``` 1194 | 1195 | 修改webpack配置文件 1196 | 1197 | ``` 1198 | // 编译js 1199 | { 1200 | test: /\.js$/, 1201 | exclude: /(node_modules|bower_components)/, 1202 | use: [ 1203 | { 1204 | loader: 'babel-loader?cacheDirectory', // 通过cacheDirectory选项开启支持缓存 1205 | options: { 1206 | presets: ['@babel/preset-env'] 1207 | } 1208 | }, 1209 | { 1210 | loader: 'eslint-loader', 1211 | options: { 1212 | fix: true 1213 | } 1214 | } 1215 | ] 1216 | }, 1217 | ``` 1218 | 1219 | 增加.eslintrc.js文件 1220 | 1221 | ``` 1222 | /* 1223 | * ESLint的JSON文件是允许JavaScript注释的,但在gist里显示效果不好,所以我把.json文件后缀改为了.js 1224 | */ 1225 | 1226 | /* 1227 | * ESLint 配置文件优先级: 1228 | * .eslintrc.js(输出一个配置对象) 1229 | * .eslintrc.yaml 1230 | * .eslintrc.yml 1231 | * .eslintrc.json(ESLint的JSON文件允许JavaScript风格的注释) 1232 | * .eslintrc(可以是JSON也可以是YAML) 1233 | * package.json(在package.json里创建一个eslintConfig属性,在那里定义你的配置) 1234 | */ 1235 | 1236 | /* 1237 | * 你可以通过在项目根目录创建一个.eslintignore文件告诉ESLint去忽略特定的文件和目录 1238 | * .eslintignore文件是一个纯文本文件,其中的每一行都是一个glob模式表明哪些路径应该忽略检测 1239 | */ 1240 | 1241 | module.exports = { 1242 | //ESLint默认使用Espree作为其解析器 1243 | //同时babel-eslint也是用得最多的解析器 1244 | //parser解析代码时的参数 1245 | "parser": "babel-eslint", 1246 | "parserOptions": { 1247 | //指定要使用的ECMAScript版本,默认值5 1248 | "ecmaVersion": 6 1249 | //设置为script(默认)或module(如果你的代码是ECMAScript模块) 1250 | // "sourceType": "script", 1251 | //这是个对象,表示你想使用的额外的语言特性,所有选项默认都是false 1252 | // "ecmafeatures": { 1253 | // //允许在全局作用域下使用return语句 1254 | // "globalReturn": false, 1255 | // //启用全局strict模式(严格模式) 1256 | // "impliedStrict": false, 1257 | // //启用JSX 1258 | // "jsx": false, 1259 | // //启用对实验性的objectRest/spreadProperties的支持 1260 | // "experimentalObjectRestSpread": false 1261 | // } 1262 | }, 1263 | //指定环境,每个环境都有自己预定义的全局变量,可以同时指定多个环境,不矛盾 1264 | "env": { 1265 | //效果同配置项ecmaVersion一样 1266 | "es6": true, 1267 | "browser": true, 1268 | "node": true, 1269 | "commonjs": false, 1270 | "mocha": true, 1271 | "jquery": true, 1272 | //如果你想使用来自某个插件的环境时,确保在plugins数组里指定插件名 1273 | //格式为:插件名/环境名称(插件名不带前缀) 1274 | // "react/node": true 1275 | }, 1276 | 1277 | //指定环境为我们提供了预置的全局变量 1278 | //对于那些我们自定义的全局变量,可以用globals指定 1279 | //设置每个变量等于true允许变量被重写,或false不允许被重写 1280 | "globals": { 1281 | "globalVar1": true, 1282 | "globalVar2": false 1283 | }, 1284 | 1285 | //ESLint支持使用第三方插件 1286 | //在使用插件之前,你必须使用npm安装它 1287 | //全局安装的ESLint只能使用全局安装的插件 1288 | //本地安装的ESLint不仅可以使用本地安装的插件还可以使用全局安装的插件 1289 | //plugin与extend的区别:extend提供的是eslint现有规则的一系列预设 1290 | //而plugin则提供了除预设之外的自定义规则,当你在eslint的规则里找不到合适的的时候 1291 | //就可以借用插件来实现了 1292 | "plugins": [ 1293 | // "eslint-plugin-airbnb", 1294 | //插件名称可以省略eslint-plugin-前缀 1295 | // "react" 1296 | ], 1297 | 1298 | //具体规则配置 1299 | //off或0--关闭规则 1300 | //warn或1--开启规则,警告级别(不会导致程序退出) 1301 | //error或2--开启规则,错误级别(当被触发的时候,程序会退出) 1302 | "rules": { 1303 | "eqeqeq": "warn", 1304 | //你也可以使用对应的数字定义规则严重程度 1305 | "curly": 2, 1306 | //如果某条规则有额外的选项,你可以使用数组字面量指定它们 1307 | //选项可以是字符串,也可以是对象 1308 | "quotes": ["error", "double"], 1309 | "one-var": ["error", { 1310 | "var": "always", 1311 | "let": "never", 1312 | "const": "never" 1313 | }], 1314 | //配置插件提供的自定义规则的时候,格式为:不带前缀插件名/规则ID 1315 | // "react/curly": "error" 1316 | }, 1317 | 1318 | //ESLint支持在配置文件添加共享设置 1319 | //你可以添加settings对象到配置文件,它将提供给每一个将被执行的规则 1320 | //如果你想添加的自定义规则而且使它们可以访问到相同的信息,这将会很有用,并且很容易配置 1321 | "settings": { 1322 | "sharedData": "Hello" 1323 | }, 1324 | 1325 | //Eslint检测配置文件步骤: 1326 | //1.在要检测的文件同一目录里寻找.eslintrc.*和package.json 1327 | //2.紧接着在父级目录里寻找,一直到文件系统的根目录 1328 | //3.如果在前两步发现有root:true的配置,停止在父级目录中寻找.eslintrc 1329 | //4.如果以上步骤都没有找到,则回退到用户主目录~/.eslintrc中自定义的默认配置 1330 | "root": true, 1331 | 1332 | //extends属性值可以是一个字符串或字符串数组 1333 | //数组中每个配置项继承它前面的配置 1334 | //可选的配置项如下 1335 | //1.字符串eslint:recommended,该配置项启用一系列核心规则,这些规则报告一些常见问题,即在(规则页面)中打勾的规则 1336 | //2.一个可以输出配置对象的可共享配置包,如eslint-config-standard 1337 | //可共享配置包是一个导出配置对象的简单的npm包,包名称以eslint-config-开头,使用前要安装 1338 | //extends属性值可以省略包名的前缀eslint-config- 1339 | //3.一个输出配置规则的插件包,如eslint-plugin-react 1340 | //一些插件也可以输出一个或多个命名的配置 1341 | //extends属性值为,plugin:包名/配置名称 1342 | //4.一个指向配置文件的相对路径或绝对路径 1343 | //5.字符串eslint:all,启用当前安装的ESLint中所有的核心规则 1344 | //该配置不推荐在产品中使用,因为它随着ESLint版本进行更改。使用的话,请自己承担风险 1345 | "extends": [ 1346 | "standard" 1347 | ] 1348 | } 1349 | 1350 | ``` 1351 | 1352 | 增加.eslintignore文件 1353 | 1354 | ``` 1355 | /dist/ 1356 | /node_modules/ 1357 | /*.js 1358 | 1359 | ``` 1360 | 1361 | 1362 | ## webpack配置resolve模块解析 1363 | 1364 | 配置alias方便路径的检索效率。 1365 | 配置文件默认扩展名,import时候自动匹配。 1366 | 1367 | ``` 1368 | resolve: { 1369 | extensions: ['.js', '.vue', '.json'], 1370 | alias: { 1371 | '@': path.resolve(__dirname, 'src/') 1372 | } 1373 | }, 1374 | ``` 1375 | 1376 | ## webpack配置外部扩展externals 1377 | 1378 | externals选项可以提供排除打包某些依赖到boundle的方法.例如我们想通过CDN引入jQuery而不是把jQuery打包到boudle。 1379 | 1380 | 这里以jQuery为例: 1381 | 1382 | 修改配置文件 1383 | ``` 1384 | // 配置外部依赖不会打包到boudle 1385 | externals: { 1386 | jquery: 'jQuery' 1387 | }, 1388 | ``` 1389 | 1390 | 在模板文件引入CDN 1391 | ``` 1392 | // index.html 1393 | 1394 | 1395 | 1396 | 1397 | 1398 | 1399 | 1400 | 1401 | 1402 | 1403 | 1407 | <%= htmlWebpackPlugin.options.title %> 1408 | 1409 | 1410 | 1411 |
1412 | 1413 |
1414 | 1415 | 1416 | 1417 | 1418 | ``` 1419 | 1420 | 在index.js使用jquery 1421 | 1422 | ``` 1423 | import $ from 'jquery' 1424 | 1425 | // 测试外部扩展配置 1426 | $(function () { 1427 | $('.logo').click(function () { 1428 | console.log('click') 1429 | }) 1430 | }) 1431 | ``` 1432 | 1433 | 1434 | 1435 | 1436 | 1437 | ## webpack打包报表分析以及优化 1438 | 1439 | 1440 | ``` 1441 | npm i -D webpack-bundle-analyzer 1442 | ``` 1443 | 1444 | 我单独配置了一个命令进行打包分析: 1445 | 1446 | 1447 | ``` 1448 | "build:report": "cross-env NODE_ENV=production npx webpack --progress --config webpack.analysis.config.js" 1449 | ``` 1450 | 1451 | 当然你其实完全可以通过传参数配置集成到prod那个配置文件如: 1452 | 1453 | ``` 1454 | "build:report": "npm run build --report" 1455 | ``` 1456 | 1457 | 然后在prod配置环境中如果参数判断: 1458 | 1459 | ``` 1460 | // 伪代码 1461 | if (config.build.bundleAnalyzerReport) { 1462 | var BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 1463 | webpackConfig.plugins.push(new BundleAnalyzerPlugin()) 1464 | } 1465 | ``` 1466 | 1467 | 这里给出我的方案 1468 | 1469 | ``` 1470 | const merge = require('webpack-merge') 1471 | const prodWebpackConfig = require('./webpack.prod.config.js') 1472 | const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin 1473 | 1474 | module.exports = merge(prodWebpackConfig, { 1475 | plugins: [ 1476 | new BundleAnalyzerPlugin() // 打包分析 1477 | ] 1478 | }) 1479 | 1480 | ``` 1481 | 1482 | 1483 | # 构建优化/打包优化配置 1484 | 1485 | 1486 | # 支持commitlint, eslint hooks 1487 | --------------------------------------------------------------------------------