這篇文章將為大家詳細講解有關webpack4.0+vue2.0如何利用批處理生成前端單頁或多頁應用,小編覺得挺實用的,因此分享給大家做個參考,希望大家閱讀完這篇文章后可以有所收獲。
批處理
前端現在在做項目的時候大多數遇到的都是單頁面應用,但有時需要做多頁面的時候,會把單頁拿過來修改成多頁面,如果代碼多了,對單頁或多頁的配置可能會混亂,那么有沒有更好的方式能把單頁面和多頁面不同的配置代碼分開,能更清楚的分辯他們的區別,這里是利用 批處理 對前端構建進行部署 git地址 目錄分為三塊
single //單頁代碼 share // 共用代碼 many //多頁代碼
只需要用到 批處理 對其中兩者進行合并就能生成想要的單頁或多頁應用,提示需要安裝國內的 npm淘寶鏡像
如果未安裝的需要自行修改build.bat里的命令行 call cnpm install 為 call npm install
如下所示:

先選擇存放路徑,輸入項目名,選擇要生成的是單頁還是多頁

這里以單頁為示例,其實就是簡單的對文件進行復制,復制完成后會自動安裝依賴

安裝完依賴后還會自動運行項目 如上開啟的項目端口為8080
目錄如下

webpack4 共同配置(share)
這里用到了最新的webpack4.0,它簡化了很多配置,多線程輸出,更快的構建能力,大大提高了開發的效率
首先看下配置文件 config.js
const path = require('path'),
config = {
//開發環境配置
dev: {
port: 8080,
// 接口代理
proxyTable: {
'/v2': {
target: 'https://api.douban.com',
changeOrigin: true
},
},
},
//生產環境配置
build: {
packName: 'myProjcet', //項目打包后名稱
outputPath: '', //打包后項目輸出路徑
templatePath: 'template.html', //html模版路徑,基于根路徑下
htmlShortPath: '/', //html文件輸出路徑, 基于outputPath
resourcesPath: '', //最終打包路徑
resourcesShortPath: 'resources', //資源目錄 {packName}/resources
},
switchVal: {
to_rem: false, //是否開啟px轉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;這里有開發環境下的接口代理,
生產環境的目錄名稱和路徑
還有可選的是否轉換頁面字體為 rem 和 eslint 語法檢測
eslint 校驗是默認的規則校驗
它還有其它的三種 通用規則
可根據自身喜好去設置
然后是 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;
},
/***
* 靜態目錄存放路徑
*/
assetsPath(_path) {
return path.posix.join(config.build.resourcesShortPath, _path);
},
copyDir(source, target) {
rm('-rf', target);
mkdir('-p', target);
cp('-R', source, target);
}
}再來看在開發和生產共用的代碼 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: {
//生產環境真實路徑
name: utils.assetsPath('fonts/[name].[hash:7].[ext]')
}
}]
},
{
test: /\.(png|jpe?g|gif|svg|webp)(\?.*)?$/,
use: {
loader: 'url-loader',
options: {
limit: 10000,
//生產環境真實路徑
name: utils.assetsPath('image/[name].[hash:7].[ext]')
}
}
},
]嗯都給了注釋,要注意的是 css、less、scss 的loader順序,不要寫反因為他是從前往后這樣編譯的 如果找不到前面的后面的loader也就無法執行 js 的loader用了一段這個
!process.env.NODE_ENV ? 'happypack/loader?id=happy-babel' : 'babel-loader',
因為在生產環境下打包時 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}),這個已經整合到 splitChunks 里面去了,再用的話就會沖突報錯
因為之前沒有留意 用3升4的過程中沒有刪除它,所以大家要重新配置4的時候還是重新一步步配置,否則很多報錯都莫名其妙,接著往下看
if (!process.env.NODE_ENV) {
for (let i = 1; i < 3; i++) {
//使用mini-css-extract-plugin在生產環境要把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'),
})
)
}在 生產環境 下原來是用 ExtractTextPlugin 插件現在都改成了 MiniCssExtractPlugin
for循環里面主要是把 vue、css、less、scss 的第一個數組 style-loader 覆蓋成 MiniCssExtractPlugin 否則會有沖突,
自動添加前綴的 postcss-loader 要放到最后面,這也是執行順序的問題
在項目最外層要增加一個 postcss.config.js 內容是
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轉rem 和 eslint 語法檢查,是否開啟和關閉是在 config.js 里設置
build.js 是這里生產打包,操作都是先清空原來的輸出目錄,復制靜態文件到輸出目錄 然后打包
const spinner = ora("開始構建生產環境.....");
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;
// 輸出編譯結果
process.stdout.write(stats.toString({
colors: true,
modules: false,
children: false,
chunks: false,
chunkModules: false,
timings: false
}) + "\n\n");
});以上就是使用單頁或多頁共同的代碼塊
webpack4 單頁配置(single)
單頁應用的目錄結構主要是這樣的,和一般開發中的 vue 項目結構一樣
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 現在就先說下 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 ,兩種模式在不同環境下都做了優化操作,想要訪問這兩種模式還是需要用到
process.env.NODE_ENV
關于頁面熱加載直接使用webpack自帶的熱加載功能 HotModuleReplacementPlugin 然后和入口文件 src/index.js 做一個合并
["webpack-hot-middleware/client?noInfo=true&reload=true"].concat("./src/index.js")后面的 noinfo 和 reload 是可配置的,如果想繼續增加參數可往這里添加, 傳送門
然后開啟熱加載 devServer: { inline: true }
在 output 里的path路徑我指向的是打包輸出路徑,webpack開發環境 是打包到內存的并不是真的打包,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
}
}這是符合合并規則條件的共同設置
chunks: 'initial', minChunks: 2, maxInitialRequests: 5, minSize: 30000, // maxInitialRequests: 3, automaticNameDelimiter: '~',
也可以把他們拎到具體的合并對象下去做單獨的規則設置
然后在 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 它有如下三個模式,可自行調整
async表示只從異步加載得模塊(動態加載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);
//處理本地開發環境下的代理接口
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);
// 靜態資源目錄 指向static目錄
app.use(express.static('./static'));
app.get('/*', function(req, res) {
res.render('index');
});
//無路由時跳轉404
app.get('*', function(req, res) {
res.render('404');
})
app.listen(port, function() {
console.log('node啟動 正在監聽端口:', port)
})這里利用 nodejs 調用模板進行頁面渲染
app.set('views', path.resolve(__dirname, 'views'));指向的是當前 build 下的 views 目錄下的html文件,
開啟熱更新和開發接口代理
app.use(devMiddleware); app.use(hotMiddleware);
app.use(express.static('./static')); 指向本地的靜態資源
比如本地的圖片路徑是 /images/jpge.jpg ,
在開發環境下訪問就會變成 http://localhost:8080/static/images/jpge.jpg ,
app.get('/*', function(req, res) {
res.render('index');
});把所有路徑直接指向到 views/index.html 下 文件內容如下
<body> <!--開發環境--> <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 做模板的映射,當真正在開發環境下使用 (template.html) 模板是這樣子的
<body> <!--生產環境--> <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)
多頁應用的目錄結構
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目錄下的一級目錄做為路由
//這個獲取的是內存路徑
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 他是插入模板的一個變量,為區分開發和生產環境路徑
<body> <!--生產環境--> <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>
關于“webpack4.0+vue2.0如何利用批處理生成前端單頁或多頁應用”這篇文章就分享到這里了,希望以上內容可以對大家有一定的幫助,使各位可以學到更多知識,如果覺得文章不錯,請把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。