webpack4.0+vue2.0利用批處理生成前端單頁或多頁應用的方法
批處理
前端現(xiàn)在在做項目的時候大多數(shù)遇到的都是單頁面應用,但有時需要做多頁面的時候,會把單頁拿過來修改成多頁面,如果代碼多了,對單頁或多頁的配置可能會混亂,那么有沒有更好的方式能把單頁面和多頁面不同的配置代碼分開,能更清楚的分辯他們的區(qū)別,這里是利用 批處理
對前端構(gòu)建進行部署 git地址 目錄分為三塊
single //單頁代碼 share // 共用代碼 many //多頁代碼
只需要用到 批處理
對其中兩者進行合并就能生成想要的單頁或多頁應用,提示需要安裝國內(nèi)的 npm淘寶鏡像
如果未安裝的需要自行修改build.bat里的命令行 call cnpm install
為 call npm install
如下所示:
先選擇存放路徑,輸入項目名,選擇要生成的是單頁還是多頁
這里以單頁為示例,其實就是簡單的對文件進行復制,復制完成后會自動安裝依賴
安裝完依賴后還會自動運行項目 如上開啟的項目端口為8080
目錄如下
webpack4 共同配置(share)
這里用到了最新的webpack4.0,它簡化了很多配置,多線程輸出,更快的構(gòu)建能力,大大提高了開發(fā)的效率
首先看下配置文件 config.js
const path = require('path'), config = { //開發(fā)環(huán)境配置 dev: { port: 8080, // 接口代理 proxyTable: { '/v2': { target: 'https://api.douban.com', changeOrigin: true }, }, }, //生產(chǎn)環(huán)境配置 build: { packName: 'myProjcet', //項目打包后名稱 outputPath: '', //打包后項目輸出路徑 templatePath: 'template.html', //html模版路徑,基于根路徑下 htmlShortPath: '/', //html文件輸出路徑, 基于outputPath resourcesPath: '', //最終打包路徑 resourcesShortPath: 'resources', //資源目錄 {packName}/resources }, switchVal: { to_rem: false, //是否開啟px轉(zhuǎn)rem to_eslint: false, //是否開啟eslint語法檢測 }, }; //輸出的目錄 config.build.outputPath = path.resolve(__dirname, '../../dist/', config.build.packName); //最終輸出目錄項目存放路徑 config.build.resourcesPath = path.join(config.build.outputPath, config.build.resourcesShortPath); module.exports = config;
這里有開發(fā)環(huán)境下的接口代理,
生產(chǎn)環(huán)境的目錄名稱和路徑
還有可選的是否轉(zhuǎn)換頁面字體為 rem
和 eslint
語法檢測
eslint
校驗是默認的規(guī)則校驗
它還有其它的三種 通用規(guī)則
可根據(jù)自身喜好去設置
然后是 utils.js
工具方法
module.exports = { /*** * 獲取src一級目錄 */ getFiles() { const files = glob.sync('src/**/'), arr = []; files.forEach((filepath) => { let name = filepath.split('/')[1]; if (name) { arr.push(...[name]); } }) let obj = {}; if (arr.length) { [...new Set(arr)].map(item => { obj[`@${item}`] = path.join(__dirname, `../src/${item}`); }) } return obj }, /** * 多頁面命名 獲取每個多頁對應的js名命名 * **/ getFileName() { let fileName = glob.sync('src/**/index.js'); entryArr = {}; fileName.forEach(function(path) { let arr = path.split('/'); let name = arr[arr.length - 2]; entryArr[name] = './' + path; }) return entryArr; }, /*** * 靜態(tài)目錄存放路徑 */ assetsPath(_path) { return path.posix.join(config.build.resourcesShortPath, _path); }, copyDir(source, target) { rm('-rf', target); mkdir('-p', target); cp('-R', source, target); } }
再來看在開發(fā)和生產(chǎn)共用的代碼 webpack.base.conf.js
首先看下一些基本的對 vue、css、js
這些loader的操作
rules: [ { test: /\.vue$/, loader: 'vue-loader', }, { test: /\.css$/, use: ['style-loader', 'css-loader'] }, { test: /\.less$/, use: ['style-loader', 'css-loader', 'less-loader'] }, { test: /\.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }, { test: /\.js$/, loader: !process.env.NODE_ENV ? 'happypack/loader?id=happy-babel' : 'babel-loader', //loader: 'babel-loader', exclude: /(node_modules|lib)/, include: [ // 表示只解析以下目錄,減少loader處理范圍 path.resolve(__dirname, '../src'), ], }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, use: [{ loader: 'file-loader', options: { //生產(chǎn)環(huán)境真實路徑 name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } }] }, { test: /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/, use: { loader: 'url-loader', options: { limit: 10000, //生產(chǎn)環(huán)境真實路徑 name: utils.assetsPath('image/[name].[hash:7].[ext]') } } }, ]
嗯都給了注釋,要注意的是 css、less、scss
的loader順序,不要寫反因為他是從前往后這樣編譯的 如果找不到前面的后面的loader也就無法執(zhí)行 js
的loader用了一段這個
!process.env.NODE_ENV ? 'happypack/loader?id=happy-babel' : 'babel-loader',
因為在生產(chǎn)環(huán)境下打包時 js
loader的編譯會很慢,所以開啟了多線程去處理 js
loader的編譯
HappyPack = require('happypack'), os = require('os'), happyThreadPool = HappyPack.ThreadPool({ size: os.cpus().length }), //利用多線程解決js loader編譯過程耗時 除scss無法使用 css、vue都可使用 (webpack4本來就是多線程) createHappyPlugin = (id, loaders) => new HappyPack({ id: id, loaders: loaders, threadPool: happyThreadPool, verbose: true, //允許 HappyPack 輸出日志 });
需要在 plugins
下加上下面這段
createHappyPlugin('happy-babel', [{ loader: 'babel-loader', options: { babelrc: true, cacheDirectory: true // 啟用緩存 } }]),
happy-babel
就是找到上面loader的id,但因為webpack4本來就是多線程的,這樣做可能多此一舉,暫時沒有測試過量大時編譯效果
還有這個
new VueLoaderPlugin()
在 vue-loader
版本為15.0以后都要加上
其它在升級到webpack4.0后還是有不少的坑,
就比如4之前可用的合并加載文件
new webpack.optimize.MinChunkSizePlugin({minChunkSize: 30000}),
這個已經(jīng)整合到 splitChunks
里面去了,再用的話就會沖突報錯
因為之前沒有留意 用3升4的過程中沒有刪除它,所以大家要重新配置4的時候還是重新一步步配置,否則很多報錯都莫名其妙,接著往下看
if (!process.env.NODE_ENV) { for (let i = 1; i < 3; i++) { //使用mini-css-extract-plugin在生產(chǎn)環(huán)境要把style-loader覆蓋,它們會有沖突 config.module.rules[i].use[0] = { loader: MiniCssExtractPlugin.loader, }; //自動添加樣式補全放 config.module.rules[i].use.splice(2, 0, 'postcss-loader'); } //css樣式合并 config.plugins.push( new MiniCssExtractPlugin({ filename: utils.assetsPath('css/[name].[chunkhash:8].css'), }) ) }
在 生產(chǎn)環(huán)境 下原來是用 ExtractTextPlugin
插件現(xiàn)在都改成了 MiniCssExtractPlugin
for循環(huán)里面主要是把 vue、css、less、scss
的第一個數(shù)組 style-loader
覆蓋成 MiniCssExtractPlugin
否則會有沖突,
自動添加前綴的 postcss-loader
要放到最后面,這也是執(zhí)行順序的問題
在項目最外層要增加一個 postcss.config.js
內(nèi)容是
module.exports = { plugins: [ require('autoprefixer')({ browsers: ['last 20 versions'] }) ] }
require的是一個自動補全css前綴的插件 last 20 versions
指的是兼容主流瀏覽器最近的20個版本,當然如果想要兼容到某個瀏覽器的特定版本也可以這樣寫
'last 10 Chrome versions', 'last 5 Firefox versions', 'Safari >= 6', 'ie> 8
接下來是前面提過的 px轉(zhuǎn)rem
和 eslint
語法檢查,是否開啟和關閉是在 config.js
里設置
build.js
是這里生產(chǎn)打包,操作都是先清空原來的輸出目錄,復制靜態(tài)文件到輸出目錄 然后打包
const spinner = ora("開始構(gòu)建生產(chǎn)環(huán)境....."); spinner.start(); //清空輸出目錄 rm("-rf", config.build.outputPath); //復制static到輸出目錄 utils.copyDir("./static/*", config.build.resourcesPath); webpack(webpackConfig).run((err, stats) => { spinner.stop(); if (err) throw err; // 輸出編譯結(jié)果 process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false, timings: false }) + "\n\n"); });
以上就是使用單頁或多頁共同的代碼塊
webpack4 單頁配置(single)
單頁應用的目錄結(jié)構(gòu)主要是這樣的,和一般開發(fā)中的 vue
項目結(jié)構(gòu)一樣
build --views --index.html --404.html --build.js --config.js --dev-server.js --utils.js --webpack.base.conf.js --webpack.dev.conf.js --webpack.prod.conf.js src --conponents --css --font --images --mixins --pages //頁面目錄 --router --store --App.vue --index.js static --jquery mode_modules
看build里的配置文件,前面講過了 build.js、config.js、utils.js、webpack.prod.conf.js
現(xiàn)在就先說下 webpack.dev.conf.js
const webpackConfig = merge(baseWebpackConfig, { mode: "development", entry: ["webpack-hot-middleware/client?noInfo=true&reload=true"].concat("./src/index.js"), devtool: "eval-source-map", output: { path: config.build.outputPath, filename: "index.js", }, module: { rules: [] }, resolve: {}, plugins: [ new webpack.HotModuleReplacementPlugin(), new Jarvis({ port: 1337 }) ], devServer: { inline: true }, });
webpack4.0新增了一個 mode為development/production
,兩種模式在不同環(huán)境下都做了優(yōu)化操作,想要訪問這兩種模式還是需要用到
process.env.NODE_ENV
關于頁面熱加載直接使用webpack自帶的熱加載功能 HotModuleReplacementPlugin
然后和入口文件 src/index.js
做一個合并
["webpack-hot-middleware/client?noInfo=true&reload=true"].concat("./src/index.js")
后面的 noinfo
和 reload
是可配置的,如果想繼續(xù)增加參數(shù)可往這里添加, 傳送門
然后開啟熱加載 devServer: { inline: true }
在 output
里的path路徑我指向的是打包輸出路徑,webpack開發(fā)環(huán)境 是打包到內(nèi)存的并不是真的打包,filename是給了個固定的 index.js
這個是要寫到 html
里做為整個項目的入口,也就是說整個項目運行就靠這個 index.js
,
在plugins里有一個 new Jarvis
這里的端口是1337,項目運行后可以打開這個端口來看下文件大小,項目運行是否出錯等等, 這個可視化窗口功能還不錯,適合有雙屏的同學
接下來看下 webpack.prod.conf.js
const webpackConfig = { entry: { index: './src/index.js' }, //webpack4默認會去查找./src/index.js output: { path: config.build.outputPath, publicPath: '/', filename: utils.assetsPath('js/[name].[chunkhash:8].js'), chunkFilename: utils.assetsPath('js/[name].[chunkhash:8].js') }, mode: 'production', devtool: 'false', module: { rules: [] }, optimization: { runtimeChunk: { //獲取頁面共同引用的代碼 name: "manifest" }, splitChunks: { chunks: 'initial', minChunks: 2, maxInitialRequests: 5, minSize: 30000, // maxInitialRequests: 3, automaticNameDelimiter: '~', cacheGroups: { vendors: { name: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10, enforce: true, }, default: { test: /[\\/]src[\\/]/, priority: -20, reuseExistingChunk: true } } } }, plugins: [ new WebpackBar({ minimal: false, }), new HtmlWebpackPlugin({ filename: path.join(config.build.htmlShortPath, 'index.html'), template: config.build.templatePath, inject: true, chunks: ['manifest', 'index'], // 引入index.js minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: false } }), //css壓縮 new OptimizeCssAssetsPlugin({ assetNameRegExp: /\.css$/g, cssProcessor: require('cssnano'), cssProcessorOptions: { safe: true, discardComments: { removeAll: true } }, canPrint: true }), ] };
這里說下在output下的publicPath,如果要把打包后的文件指向一個相對路徑要加上 /
要不然生成出來的的入口文件會變成 resources/js/xxx.js
而不是我們期待的 /resources/js/xxx.js
再則圖片的路徑也會變成 resources/image/...png
,這樣是無效的路徑,當然這還是要看你用的是相對路徑還是絕對路徑了
來看下 optimization
這個東西,這是webpack4新加的功能用于代碼的合并策略,這里是對兩個地方的js進行合并一個是npm包一個是項目下的代碼
cacheGroups: { vendors: { name: 'vendors', test: /[\\/]node_modules[\\/]/, priority: -10, enforce: true, }, default: { test: /[\\/]src[\\/]/, priority: -20, reuseExistingChunk: true } }
這是符合合并規(guī)則條件的共同設置
chunks: 'initial', minChunks: 2, maxInitialRequests: 5, minSize: 30000, // maxInitialRequests: 3, automaticNameDelimiter: '~',
也可以把他們拎到具體的合并對象下去做單獨的規(guī)則設置
然后在 plugins
下引用上面的合并后的js
new HtmlWebpackPlugin({ filename: path.join(config.build.htmlShortPath, 'index.html'), template: config.build.templatePath, inject: true, chunks: ['manifest', 'index'], // 引入index.js minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: false } }),
chunks
它有如下三個模式,可自行調(diào)整
- async表示只從異步加載得模塊(動態(tài)加載import())里面進行拆分
- initial表示只從入口模塊進行拆分
- all表示以上兩者都包括
再看下 dev-server.js
啟動項入口
let port = process.env.PORT || config.dev.port; const app = express(); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({ extended: true })); app.engine('html', ejs.__express); app.set('view engine', 'html'); app.set('views', path.resolve(__dirname, 'views')); app.use(compression()); //開啟gzip //webpack編譯器 const compiler = webpack(webpackConfig); //webpack-dev-server 中間件 const devMiddleware = require('webpack-dev-middleware')(compiler, { //這里必填 與webpack配置的路徑相同 publicPath: webpackConfig.output.publicPath, stats: { colors: true, chunks: false, } }) //熱更新中間件 const hotMiddleware = require('webpack-hot-middleware')(compiler); //處理本地開發(fā)環(huán)境下的代理接口 Object.keys(config.dev.proxyTable).forEach(function(context) { const options = config.dev.proxyTable[context]; if (typeof options === 'string') { options = { target: options } } if (~context.indexOf(',')) { context = context.split(','); } app.use(proxyMiddleware(context, options)); }) app.use(devMiddleware); app.use(hotMiddleware); // 靜態(tài)資源目錄 指向static目錄 app.use(express.static('./static')); app.get('/*', function(req, res) { res.render('index'); }); //無路由時跳轉(zhuǎn)404 app.get('*', function(req, res) { res.render('404'); }) app.listen(port, function() { console.log('node啟動 正在監(jiān)聽端口:', port) })
這里利用 nodejs
調(diào)用模板進行頁面渲染
app.set('views', path.resolve(__dirname, 'views'));
指向的是當前 build
下的 views
目錄下的html文件,
開啟熱更新和開發(fā)接口代理
app.use(devMiddleware); app.use(hotMiddleware);
app.use(express.static('./static'));
指向本地的靜態(tài)資源
比如本地的圖片路徑是 /images/jpge.jpg
,
在開發(fā)環(huán)境下訪問就會變成 http://localhost:8080/static/images/jpge.jpg
,
app.get('/*', function(req, res) { res.render('index'); });
把所有路徑直接指向到 views/index.html
下 文件內(nèi)容如下
<body> <!--開發(fā)環(huán)境--> <div id="app"></div> <script type="text/javascript" src="lib/jquery/jquery.min.js"></script> <script type="text/javascript" src="lib/bootstrap/js/bootstrap.min.js"></script> <script type="text/javascript" src="index.js"></script> </body>
index.js
就是之前的入口文件,必須要寫進html文件里的,因為沒有用 HtmlWebpackPlugin
做模板的映射,當真正在開發(fā)環(huán)境下使用 (template.html)
模板是這樣子的
<body> <!--生產(chǎn)環(huán)境--> <div id="app"></div> <script type="text/javascript" src="/resources/lib/jquery/jquery.min.js"></script> <script type="text/javascript" src="/resources/lib/bootstrap/js/bootstrap.min.js"></script> </body>
所以分了兩個模板去渲染頁面
webpack4 多頁配置(single)
多頁應用的目錄結(jié)構(gòu)
build --views --index.html --404.html --build.js --config.js --dev-server.js --utils.js --webpack.base.conf.js --webpack.dev.conf.js --webpack.prod.conf.js src --conponents --css --font --images --mixins --pages //頁面目錄 --new --index.js //入口 --new.vue static --jquery mode_modules
build目錄下有三個文件有些改動
dev-server.js
去掉了視圖目錄指向
因為是多頁的,這里是獲取src目錄下的一級目錄做為路由
//這個獲取的是內(nèi)存路徑 app.get('/:viewname?', function(req, res, next) { var viewname = req.params.viewname ? req.params.viewname + '.html' : 'main.html'; var filepath = path.join(compiler.outputPath, viewname); compiler.outputFileSystem.readFile(filepath, function(err, result) { if (err) { res.send('can\'t not find the file: ' + filepath).end; return; } res.set('content-type', 'text/html'); res.send(result); res.end(); }); });
然后是 webpack.dev.conf.js
里加了這一段
let entryObj = utils.getFileName(); Object.keys(entryObj).forEach((name) => { webpackConfig.entry[name] = ['webpack-hot-middleware/client?noInfo=true&reload=true'].concat(entryObj[name]); let plugin = new htmlWebpackPlugin({ filename: name + '.html', template: config.build.templatePath, inject: true, chunks: [name] }); webpackConfig.plugins.push(plugin); })
獲取src目錄下的每個文件做為入口進行模板渲染
同樣在 webpackprod.conf.js
也需要加上
let entryObj = utils.getFileName(); Object.keys(entryObj).forEach((name) => { webpackConfig.entry[name] = entryObj[name]; let plugin = new HtmlWebpackPlugin({ chunks: ['manifest', name], filename: name + '.html', template: config.build.templatePath, inject: true, environment: 'resources', minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: false } }); webpackConfig.plugins.push(plugin); })
這里多了一個 environment
他是插入模板的一個變量,為區(qū)分開發(fā)和生產(chǎn)環(huán)境路徑
<body> <!--生產(chǎn)環(huán)境--> <div id="app"></div> <script type="text/javascript" src="<%= htmlWebpackPlugin.options.environment %>/lib/jquery/jquery.min.js"></script> <script type="text/javascript" src="<%= htmlWebpackPlugin.options.environment %>/lib/bootstrap/js/bootstrap.min.js"></script> </body>
以上就是本文的全部內(nèi)容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
JavaScript函數(shù)封裝隨機顏色驗證碼(完整代碼)
這篇文章主要介紹了JavaScript函數(shù)封裝隨機顏色驗證碼(完整代碼),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2021-12-12JS驗證 只能輸入小數(shù)點,數(shù)字,負數(shù)的實現(xiàn)方法
下面小編就為大家?guī)硪黄狫S驗證 只能輸入小數(shù)點,數(shù)字,負數(shù)的實現(xiàn)方法。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2016-10-10分享十三個最佳JavaScript數(shù)據(jù)網(wǎng)格庫
數(shù)據(jù)網(wǎng)格可以幫助解決在 HTML 表格上對帶有過濾、分頁、排序、搜索以及內(nèi)聯(lián)編輯這些功能特性的大量數(shù)據(jù)集的處理問題,需要的朋友可以參考下2017-04-04用js實現(xiàn)before和after偽類的樣式修改的示例代碼
本篇文章主要介紹了用js實現(xiàn)before和after偽類的樣式修改的示例代碼,具有一定的參考價值,有興趣的可以了解一下2017-09-09