摘要:通用封裝簡化配置現(xiàn)在,基本上前端的項(xiàng)目打包都會用上,因?yàn)樘峁┝藷o與倫比強(qiáng)大的功能和生態(tài)。簡化配置的一種方式是使用社區(qū)封裝好的庫,比如。封裝了的一些基礎(chǔ)配置,然后暴露一些額外配置的接口,并附加本地?cái)?shù)據(jù)模擬功能,詳情可以參考主頁。
通用、封裝、簡化 webpack 配置
現(xiàn)在,基本上前端的項(xiàng)目打包都會用上 webpack,因?yàn)?webpack 提供了無與倫比強(qiáng)大的功能和生態(tài)。但在創(chuàng)建一個(gè)項(xiàng)目的時(shí)候,總是免不了要配置 webpack,很是麻煩。
簡化 webpack 配置的一種方式是使用社區(qū)封裝好的庫,比如 roadhog。roadhog 封裝了 webpack 的一些基礎(chǔ)配置,然后暴露一些額外配置的接口,并附加本地?cái)?shù)據(jù)模擬功能(mock),詳情可以參考 roadhog 主頁。
另一種方式是自己封裝 webpack,這樣做自己能夠更好的掌控項(xiàng)目。
1. 要封裝哪些功能一般搭建一個(gè)項(xiàng)目至少需要兩種功能:本地開發(fā)調(diào)試、構(gòu)建產(chǎn)品代碼。
其他的諸如測試、部署到服務(wù)器、代碼檢查、格式優(yōu)化等功能則不在這篇文章講解范圍,如果有意了解,可以查看我的其他文章。
2. 基礎(chǔ)配置 2.1 目錄結(jié)構(gòu)(示例,配合后面的代碼講解)package.json dev.js # 本地開發(fā)腳本 build.js # 產(chǎn)品構(gòu)建腳本 analyze.js # 模塊大小分析(可選) # 單頁面結(jié)構(gòu) src/ # 源代碼目錄 - index.js # js 入口文件 - index.html # html 入口文件 - ... # 其他文件 # 多頁面結(jié)構(gòu) src/ # 源代碼目錄 - home/ # home 頁面工作空間 - index.js # home 頁面 js 入口文件 - index.html # home 頁面 html 入口文件 - ... # home 頁面其他文件 - explore/ # explore 頁面工作空間 - index.js # explore 頁面 js 入口文件 - index.html # explore 頁面 html 入口文件 - ... # explore 頁面其他文件 - about/ # about 目錄 - company # about/company 頁面工作空間 - index.js # about/company 頁面 js 入口文件 - index.html # about/company 頁面 html 入口文件 - ... # about/company 頁面其他文件 - platform # about/platform 頁面工作空間 - index.js # about/platform 頁面 js 入口文件 - index.html # about/platform 頁面 html 入口文件 - ... # about/platform 頁面其他文件 - ... # 更多頁面2.2 基礎(chǔ) npm 包
# package.json "devDependencies": { "@babel/core": "^7.1.2", # babel core "@babel/plugin-syntax-dynamic-import": "^7.0.0", # import() 函數(shù)支持 "@babel/plugin-transform-react-jsx": "^7.0.0", # react jsx 支持 "@babel/preset-env": "^7.1.0", # es6+ 轉(zhuǎn) es5 "@babel/preset-flow": "^7.0.0", # flow 支持 "@babel/preset-react": "^7.0.0", # react 支持 "autoprefixer": "^9.1.5", # css 自動添加廠家前綴 -webkit-, -moz- "babel-loader": "^8.0.4", # webpack 加載 js 的 loader "babel-plugin-component": "^1.1.1", # 如果使用 element ui,需要用到這個(gè) "babel-plugin-flow-runtime": "^0.17.0", # flow-runtime 支持 "babel-plugin-import": "^1.9.1", # 如果使用 ant-design,需要用到這個(gè) "browser-sync": "^2.24.7", # 瀏覽器實(shí)例組件,用于本地開發(fā)調(diào)試 "css-loader": "^1.0.0", # webpack 加載 css 的 loader "chalk": "^2.4.1", # 讓命令行的信息有顏色 "file-loader": "^2.0.0", # webpack 加載靜態(tài)文件的 loader "flow-runtime": "^0.17.0", # flow-runtime 包 "html-loader": "^0.5.5", # webpack 加載 html 的 loader "html-webpack-include-assets-plugin": "^1.0.5", # 給 html 文件添加額外靜態(tài)文件鏈接的插件 "html-webpack-plugin": "^3.2.0", # 更方便操作 html 文件的插件 "less": "^3.8.1", # less 轉(zhuǎn) css "less-loader": "^4.1.0", # webpack 加載 less 的 loader "mini-css-extract-plugin": "^0.4.3", # 提取 css 多帶帶打包 "minimist": "^1.2.0", # process.argv 更便捷處理 "node-sass": "^4.9.3", # scss 轉(zhuǎn) css "optimize-css-assets-webpack-plugin": "^5.0.1", # 優(yōu)化 css 打包,包括壓縮 "postcss-loader": "^3.0.0", # 對 css 進(jìn)行更多操作,比如添加廠家前綴 "sass-loader": "^7.1.0", # webpack 加載 scss 的 loader "style-loader": "^0.23.0", # webpack 加載 style 的 loader "uglifyjs-webpack-plugin": "^2.0.1", # 壓縮 js 的插件 "url-loader": "^1.1.1", # file-loader 的升級版 "vue-loader": "^15.4.2", # webpack 加載 vue 的 loader "vue-template-compiler": "^2.5.17", # 配合 vue-loader 使用的 "webpack": "^4.20.2", # webpack 模塊 "webpack-bundle-analyzer": "^3.0.2", # 分析當(dāng)前打包各個(gè)模塊的大小,決定哪些需要多帶帶打包 "webpack-dev-middleware": "^3.4.0", # webpack-dev-server 中間件 "webpack-hot-middleware": "^2.24.2" # 熱更新中間件 }2.3 基本命令
# package.json "scripts": { "dev": "node dev.js", "build": "node build.js", "analyze": "node analyze.js", }
npm run dev # 開發(fā) npm run build # 構(gòu)建 npm run analyze # 模塊分析
如果需要支持多入口構(gòu)建,在命令后面添加參數(shù):
npm run dev -- home # 開發(fā) home 頁面 npm run analyze -- explore # 模塊分析 explore 頁面 # 構(gòu)建多個(gè)頁面 npm run build -- home explore about/* about/all --env test/prod
home, explore 確定構(gòu)建的頁面;about/*, about/all 指 about 目錄下所有的頁面;all, * 整個(gè)項(xiàng)目所有的頁面
有時(shí)候可能還會針對不同的服務(wù)器環(huán)境(比如測試機(jī)、正式機(jī))做出不同的構(gòu)建,可以在后面加參數(shù)
-- 用來分割 npm 本身的參數(shù)與腳本參數(shù),參考 npm - run-script 了解詳情
2.4 dev.js 配置開發(fā)一般用需要用到下面的組件:
webpack
webpack-dev-server 或 webpack-dev-middleware
webpack-hot-middleware
HotModuleReplacementPlugin
browser-sync
const minimist = require("minimist"); const webpack = require("webpack"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const devMiddleWare = require("webpack-dev-middleware"); const hotMiddleWare = require("webpack-hot-middleware"); const browserSync = require("browser-sync"); const VueLoaderPlugin = require("vue-loader/lib/plugin"); const { HotModuleReplacementPlugin } = webpack; const argv = minimist(process.argv.slice(2)); const page = argv._[0]; // 單頁面 const entryFile = `${__dirname}/src/index.js`; // 多頁面 const entryFile = `${__dirname}/src/${page}/index.js`; // 編譯器對象 const compiler = webpack({ entry: [ "webpack-hot-middleware/client?reload=true", // 熱重載需要 entryFile, ], output: { path: `${__dirname}/dev/`, // 打包到 dev 目錄 filename: "index.js", publicPath: "/dev/", }, plugins: [ new HotModuleReplacementPlugin(), // 熱重載插件 new HtmlWebpackPlugin({ // 處理 html // 單頁面 template: `${__dirname}/src/index.html`, // 多頁面 template: `${__dirname}/src/${page}/index.html`, }), new VueLoaderPlugin(), // vue-loader 所需 ], module: { rules: [ { // js 文件加載器 loader: "babel-loader", exclude: /node_modules/, options: { presets: ["@babel/preset-env", "@babel/preset-react"], plugins: [ "@babel/plugin-transform-react-jsx", "@babel/plugin-syntax-dynamic-import", ], }, test: /.(js|jsx)$/, }, { // css 文件加載器 loader: "style-loader!css-loader", test: /.css$/, }, { // less 文件加載器 loader: "style-loader!css-loader!less-loader", test: /.less$/, }, { // scss 文件加載器 loader: "style-loader!css-loader!sass-loader", test: /.(scss|sass)$/, }, { // 靜態(tài)文件加載器 loader: "url-loader", test: /.(gif|jpg|png|woff|woff2|svg|eot|ttf|ico)$/, options: { limit: 1, }, }, { // html 文件加載器 loader: "html-loader", test: /.html$/, options: { attrs: ["img:src", "link:href"], interpolate: "require", }, }, { // vue 文件加載器 loader: "vue-loader", test: /.vue$/, }, ], }, resolve: { alias: {}, // js 配置別名 modules: [`${__dirname}/src`, "node_modules"], // 模塊尋址基路徑 extensions: [".js", ".jsx", ".vue", ".json"], // 模塊尋址擴(kuò)展名 }, devtool: "eval-source-map", // sourcemap mode: "development", // 指定 webpack 為開發(fā)模式 }); // browser-sync 配置 const browserSyncConfig = { server: { baseDir: `${__dirname}/`, // 靜態(tài)服務(wù)器基路徑,可以訪問項(xiàng)目所有文件 }, startPath: "/dev/index.html", // 開啟服務(wù)器窗口時(shí)的默認(rèn)地址 }; // 添加中間件 browserSyncConfig.middleware = [ devMiddleWare(compiler, { stats: "errors-only", publicPath: "/dev/", }), hotMiddleWare(compiler), ]; browserSync.init(browserSyncConfig); // 初始化瀏覽器實(shí)例,開始調(diào)試開發(fā)2.5 build.js 配置
構(gòu)建過程中,一般會有這些過程:
提取樣式文件,多帶帶打包、壓縮、添加瀏覽器廠家前綴
對 js 在產(chǎn)品模式下進(jìn)行打包,并生成 sourcemap 文件
html-webpack-plugin 自動把打包好的樣式文件與腳本文件引用到 html 文件中,并壓縮
對所有資源進(jìn)行 hash 化處理(可選)
const minimist = require("minimist"); const webpack = require("webpack"); const chalk = require("chalk"); const autoprefixer = require("autoprefixer"); const HtmlWebpackPlugin = require("html-webpack-plugin"); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const OptimizeCssAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const VueLoaderPlugin = require("vue-loader/lib/plugin"); const {yellow, red} = chalk; const argv = minimist(process.argv.slice(2)); const pages = argv._; // ["home", "explore", "about/*", "about/all"] const allPages = getAllPages(pages); // 根據(jù) page 中的 `*, all` 等關(guān)鍵字,獲取所有真正的 pages // 單頁面,只有一個(gè)入口,所以只有一個(gè)配置文件 const config = { ... }; // 多頁面,多個(gè)入口,所有有多個(gè)配置文件 const configs = allPages.map(page => ({ // 單頁面 entry: `${__dirname}/src/index.js`, // js 入口文件 // 多頁面 entry: `${__dirname}/src/${page}/index.js`, // js 入口文件 output: { path: `${__dirname}/dist/`, // 輸出路徑 filename: "[chunkhash].js", // 輸出文件名,這里完全取 hash 值來命名 hashDigestLength: 32, // hash 值長度 publicPath: "/dist/", }, plugins: [ new MiniCssExtractPlugin({ // 提取所有的樣式文件,多帶帶打包 filename: "[chunkhash].css", // 輸出文件名,這里完全取 hash 值來命名 }), new HtmlWebpackPlugin({ // 單頁面 template: `${__dirname}/src/index.html`, // html 入口文件 // 多頁面 template: `${__dirname}/src/${page}/index.html`,// html 入口文件 minify: { // 指定如果壓縮 html 文件 removeComments: !0, collapseWhitespace: !0, collapseBooleanAttributes: !0, removeEmptyAttributes: !0, removeScriptTypeAttributes: !0, removeStyleLinkTypeAttributes: !0, minifyJS: !0, minifyCSS: !0, }, }), new VueLoaderPlugin(), // vue-loader 所需 new OptimizeCssAssetsPlugin({ // 壓縮 css cssProcessorPluginOptions: { preset: ["default", { discardComments: { removeAll: true } }], }, }), // webpack 打包的 js 文件是默認(rèn)壓縮的,所以這里不需要再額外添加 uglifyjs-webpack-plugin ], module: { rules: [ { // js 文件加載器,與 dev 一致 loader: "babel-loader", exclude: /node_modules/, options: { presets: ["@babel/preset-env", "@babel/preset-react"], plugins: [ "@babel/plugin-transform-react-jsx", "@babel/plugin-syntax-dynamic-import", ], }, test: /.(js|jsx)$/, }, { // css 文件加載器,添加了瀏覽器廠家前綴 use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { plugins: [ autoprefixer({ browsers: [ "> 1%", "last 2 versions", "Android >= 3.2", "Firefox >= 20", "iOS 7", ], }), ], }, }, ], test: /.css$/, }, { // less 文件加載器,添加了瀏覽器廠家前綴 use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { plugins: [ autoprefixer({ browsers: [ "> 1%", "last 2 versions", "Android >= 3.2", "Firefox >= 20", "iOS 7", ], }), ], }, }, "less-loader", ], test: /.less$/, }, { // scss 文件加載器,添加了瀏覽器廠家前綴 use: [ MiniCssExtractPlugin.loader, "css-loader", { loader: "postcss-loader", options: { plugins: [ autoprefixer({ browsers: [ "> 1%", "last 2 versions", "Android >= 3.2", "Firefox >= 20", "iOS 7", ], }), ], }, }, "sass-loader", ], test: /.(scss|sass)$/, }, { // 靜態(tài)文件加載器,與 dev 一致 loader: "url-loader", test: /.(gif|jpg|png|woff|woff2|svg|eot|ttf|ico)$/, options: { limit: 1, }, }, { // html 文件加載器,與 dev 一致 loader: "html-loader", test: /.html$/, options: { attrs: ["img:src", "link:href"], interpolate: "require", }, }, { // vue 文件加載器,與 dev 一致 loader: "vue-loader", test: /.vue$/, }, ], }, resolve: { alias: {}, // js 配置別名 modules: [`${__dirname}/src`, "node_modules"], // 模塊尋址基路徑 extensions: [".js", ".jsx", ".vue", ".json"], // 模塊尋址擴(kuò)展名 }, devtool: "source-map", // sourcemap mode: "production", // 指定 webpack 為產(chǎn)品模式 })); // 執(zhí)行一次 webpack 構(gòu)建 const run = (config, cb) => { webpack(config, (err, stats) => { if (err) { console.error(red(err.stack || err)); if (err.details) { console.error(red(err.details)); } process.exit(1); } const info = stats.toJson(); if (stats.hasErrors()) { info.errors.forEach(error => { console.error(red(error)); }); process.exit(1); } if (stats.hasWarnings()) { info.warnings.forEach(warning => { console.warn(yellow(warning)); }); } // 如果是多頁面,需要把 index.html => `${page}.html` // 因?yàn)槊總€(gè)頁面導(dǎo)出的 html 文件都是 index.html 如果不重新命名,會被覆蓋掉 if(cb) cb(); }); }; // 單頁面 run(config); // 多頁面 let index = 0; // go on const goon = () => { run(configs[index], () => { index += 1; if (index < configs.length) goon(); }); }; goon();2.6 analyze.js 配置
const minimist = require("minimist"); const chalk = require("chalk"); const webpack = require("webpack"); const { BundleAnalyzerPlugin } = require("webpack-bundle-analyzer"); const VueLoaderPlugin = require("vue-loader/lib/plugin"); const {yellow, red} = chalk; const argv = minimist(process.argv.slice(2)); const page = argv._[0]; // 單頁面 const entryFile = `${__dirname}/src/index.js`; // 多頁面 const entryFile = `${__dirname}/src/${page}/index.js`; const config = { entry: entryFile, output: { path: `${__dirname}/analyze/`, // 打包到 analyze 目錄 filename: "index.js", }, plugins: [ new VueLoaderPlugin(), // vue-loader 所需 new BundleAnalyzerPlugin(), // 添加插件 ], module: { rules: [ { // js 文件加載器 loader: "babel-loader", exclude: /node_modules/, options: { presets: ["@babel/preset-env", "@babel/preset-react"], plugins: [ "@babel/plugin-transform-react-jsx", "@babel/plugin-syntax-dynamic-import", ], }, test: /.(js|jsx)$/, }, { // css 文件加載器 loader: "style-loader!css-loader", test: /.css$/, }, { // less 文件加載器 loader: "style-loader!css-loader!less-loader", test: /.less$/, }, { // scss 文件加載器 loader: "style-loader!css-loader!sass-loader", test: /.(scss|sass)$/, }, { // 靜態(tài)文件加載器 loader: "url-loader", test: /.(gif|jpg|png|woff|woff2|svg|eot|ttf|ico)$/, options: { limit: 1, }, }, { // html 文件加載器 loader: "html-loader", test: /.html$/, options: { attrs: ["img:src", "link:href"], interpolate: "require", }, }, { // vue 文件加載器 loader: "vue-loader", test: /.vue$/, }, ], }, resolve: { alias: {}, // js 配置別名 modules: [`${__dirname}/src`, "node_modules"], // 模塊尋址基路徑 extensions: [".js", ".jsx", ".vue", ".json"], // 模塊尋址擴(kuò)展名 }, mode: "production", // 指定 webpack 為產(chǎn)品模式 }; webpack(config, (err, stats) => { if (err) { console.error(red(err.stack || err)); if (err.details) { console.error(red(err.details)); } process.exit(1); } const info = stats.toJson(); if (stats.hasErrors()) { info.errors.forEach(error => { console.error(red(error)); }); process.exit(1); } if (stats.hasWarnings()) { info.warnings.forEach(warning => { console.warn(yellow(warning)); }); } });2.7 擴(kuò)展配置
你可以根據(jù)需要擴(kuò)展配置,比如添加插件、加載器等,比如:
provide-plugin 可以提供一些全局模塊的導(dǎo)出,比如 jquery
define-plugin 可以動態(tài)定義一些全局變量
css-loader 可以配置成 css-modules
如果某個(gè)頁面導(dǎo)出 js bundle 很大,想分割成多個(gè)文件,可以使用 dll-plugin、split-chunks-plugin
如果想在命令行顯示構(gòu)建的進(jìn)度,可以使用 progress-plugin
3. 封裝上面的代碼可以封裝成一個(gè)全局命令,比如 lila,運(yùn)行上面的命令就可以更簡潔:
lila dev home # 開發(fā) home 頁面 lila analyze explore # 模塊分析 explore 頁面 # 構(gòu)建多個(gè)頁面 lila build home explore about/* about/all --env test/prod后續(xù)
更多博客,查看 https://github.com/senntyou/blogs
作者:深予之 (@senntyou)
版權(quán)聲明:自由轉(zhuǎn)載-非商用-非衍生-保持署名(創(chuàng)意共享3.0許可證)
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/99701.html
摘要:使用基于依賴追蹤的觀察并且使用異步隊(duì)列更新。為項(xiàng)目配置文件。為項(xiàng)目靜態(tài)資源目錄。其實(shí)個(gè)人感覺通用項(xiàng)目目錄可以很隨意的搭配,比如說之后清空目錄封裝通用組件,像是啊,滑動常用組件。 寫在前面的個(gè)人體會,大神們可以跳過 這段時(shí)間接手一個(gè)后臺管理項(xiàng)目,從最開始寫一點(diǎn)我自己的體會吧。首先Vue,Angular和React是當(dāng)今主流前端三大框架。Vue是一個(gè)用來構(gòu)建網(wǎng)頁的JS庫,相比較Angula...
摘要:前言這里筑夢師是一名正在努力學(xué)習(xí)的開發(fā)工程師目前致力于全棧方向的學(xué)習(xí)希望可以和大家一起交流技術(shù)共同進(jìn)步用簡書記錄下自己的學(xué)習(xí)歷程個(gè)人學(xué)習(xí)方法分享本文目錄更新說明目錄學(xué)習(xí)方法學(xué)習(xí)態(tài)度全棧開發(fā)學(xué)習(xí)路線很長知識拓展很長在這里收取很多人的建議以后決 前言 這里筑夢師,是一名正在努力學(xué)習(xí)的iOS開發(fā)工程師,目前致力于全棧方向的學(xué)習(xí),希望可以和大家一起交流技術(shù),共同進(jìn)步,用簡書記錄下自己的學(xué)習(xí)歷程...
閱讀 3918·2021-09-27 13:36
閱讀 4666·2021-09-22 15:12
閱讀 3107·2021-09-13 10:29
閱讀 1868·2021-09-10 10:50
閱讀 2409·2021-09-03 10:43
閱讀 551·2019-08-29 17:10
閱讀 471·2019-08-26 13:52
閱讀 3307·2019-08-23 14:37