使用webpack已经三年多的时间了,本篇文章就webpack在前端工程化中的作用结合之前项目体会做以简单总结。
webpack对开发过程中的环境构建
任何项目中基本上都是包含以下三个重要的环境:开发环境,测试环境,生产环境。在没有使用webpack之前,对于前端开发人会员几乎没有一个全面成手的工具能够快速实现开发,测试,生产环境的搭建和切换,就我自己而言,在webpack之前使用过gulp,由采用了于原生的js开发项目中的各类组件,项目运行中会依赖很多前端自定义和第三方的js,再加上项目成员各个模块中都依赖或生成大量的js,所以在开发阶段就已经暴露出项目工程中js管理混乱的问题,私用gulp配合node dev-sever 事项了可发小组内各个模块独自开发,自测后打包给我各自的js库,然后由我项目工程架构师将各个模块集成分离再次打包上线。导致每次上线会有处理不完的各种依赖,和无法分离的冗余代码,现在回想起来真是噩梦。
使用webpack构建项目有效解决以上问题,现阶段基于webpack构建前端工程已经成为前端工程化最佳模式了。
开发阶段webpack+express 搭建热启动,前后端分离实现业务。
测试阶段,前端mok数据,给据框架选择jasmine或etest进行单元测试。
上线,可选择前端打包静态资源丢给服务器,或者node中间件方式前端服务整体生成docker镜像服务器部署。
前端工程化必须要解决的问题:
1.根据项目和架构需求明确到底是属于单页面还是多页面应用,注意项目框架中的路由并不属于webpack所要控制,所谓单页面多页面指的是最终交付客户的产品使用中,会有几个入口的问题,比如真对企业开发的管理系统从登录开始就是单页面,而对于可视化应用场景,会有很多不同的独立的预案场景入口,则属于多入口应用。
明确以上这点非常重要,因为它会设计到接下来 项目工程组织结构,及优化方案的方向,比如多入口就必须考虑代码公共部分提取,但入口则重点要关注代码异步加载。
2.明确项目上线运行的浏览器环境,如果项目要求必须兼容ie6,那么你就悲剧了,基本上现代前端的高级技术基本都无法使用,不过除了某些特殊行业,大多数项目也是与时俱进ie9以上了,我其实考虑更多是各种浏览器的兼容问题,特别还未实现es6语法的浏览器,所以webpack的配置中要实现es6转es5,以及不同浏览器css的前后缀问题等等。
3.处理css,处理图片,字体库及项目中使用到的特殊文件,在webpack4后此处就是安装配置使用css-loader,style-loader,url-loader file-loader等
4.typescript,cass,less使用,对于具规模的大项目基本都要求用ts开发业务,cass或less写样式,webpack对其环境的配置构建也相对简单。
5.项目优化 因为项目有webpack进行构建,所以后期我针对工程的优化大多都是在优化webpack,比如对plugins的扩展,优化打包文件大小,提取公共部分,分割打包文件等等。
最后上传一份项目中使用的前后端分离且适配开发,生产环境的webpack模板:
packge.json:
{
"name": "webpack-3.1.8",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "webpack --env production --config webpack.common.js",
"dev": "webpack-dev-server --env development --config webpack.common.js --open"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"eslint": "^6.1.0",
"eslint-config-standard": "^13.0.1",
"eslint-loader": "^2.2.1",
"eslint-plugin-standard": "^4.0.0",
"extract-text-webpack-plugin": "^3.0.2",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10"
},
"dependencies": {
"@babel/core": "^7.5.5",
"ansi-styles": "^4.0.0",
"autoprefixer": "^9.6.1",
"babel-eslint": "^10.0.2",
"babel-loader": "^8.0.6",
"css-loader": "^3.1.0",
"escape-string-regexp": "^2.0.0",
"eslint": "^6.1.0",
"eslint-friendly-formatter": "^4.0.1",
"eslint-loader": "^2.2.1",
"express": "^4.17.1",
"extract-text-webpack-plugin": "^4.0.0-beta.0",
"html-webpack-plugin": "^3.2.0",
"jquery": "^3.4.1",
"less": "^3.9.0",
"less-loader": "^5.0.0",
"mini-css-extract-plugin": "^0.8.0",
"postcss": "^7.0.17",
"postcss-cssnext": "^3.1.0",
"postcss-loader": "^3.0.0",
"postcss-sprites": "^4.2.1",
"style-loader": "^0.23.1",
"webpack": "^4.41.2",
"webpack-cli": "^3.3.10",
"webpack-dev-middleware": "^3.7.2",
"webpack-dev-server": "^3.7.2",
"webpack-hot-middleware": "^2.25.0",
"webpack-merge": "^4.2.1"
}
}
webpack.common.js
const webpack=require('webpack');
const extractTextCss=require('extract-text-webpack-plugin');
const dev=require('./webpack.dev.js');
const pro=require('./webpack.pro.js');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var minicss=require('mini-css-extract-plugin');
const merge=require('webpack-merge');
module.exports=env=>{
//配置对象
function getcssoptions(env){
if(env==='production'){
return{
test:/\.css$/,
use:[
{
loader:minicss.loader,
},
{
loader:'css-loader',
}
]
}
}else{
return{
test:/\.css$/,
use:[
{
loader:'style-loader',
},
{
loader:'css-loader',
}
]
}
}
}
var common={
//entry:'./src/app.js',
entry:{
app:"./src/app.js",
//app2:"./src/app2.js"
},
output:{
filename:'[name].bundle.js'
},
module:{
rules: [
//js处理
{
test:/\.js$/,
use:
{
loader:'babel-loader',
}
},
//css处理
getcssoptions()
]
},
plugins:[
//提取额外css文件
new minicss({
filename:'[name].min.css'
}),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.html',
minify: {
collapseWhitespace: true
},
inject:true,
}),
]
};
//返回配置对象
return merge(env==='production'?pro:dev,common);
}
webpack.dev.js
const webpack = require('webpack')
module.exports={
devtool: 'cheap-module-source-map',
devServer: {
port: 9001,
hot:true,
hotOnly:true,
historyApiFallback:{
rewrites:[
{
from:/^\/([ -~]+)/,
to:function(context){
return "./"+context.match[1]+".html";
}
}
]
},
proxy:{
"/smartSpec":{
target:"https://mooc.study.163.com/",
changeOrigin:true,
pathRewrite:{
"^/smartSpec/qd":"/smartSpec/detail/1202816603.htm"
},
}
}
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new webpack.NamedModulesPlugin(),
]
}
webpack.pro.js
module.exports={
optimization:{
minimize:false,
splitChunks:{
name:true,
chunks:'initial',
automaticNameDelimiter:".",
minSize:0,
cacheGroups:{
modulea:{
test:/modulea/,
priority:-10
},
vendor:{
test:/[\\/]node_modules[\\/]/,
priority:-10,
name:'vendor'
}
}
},
runtimeChunk:{
name:'runtime'
}
},
plugins:[
]
}
server.js
const express=require('express');
const webpackDevMid=require('webpack-dev-middleware');
const webpackhot=require('webpack-hot-middleware');
const webpack=require('webpack');
const commonfig=require("./webpack.common.js");
const devfig=require("./webpack.dev.js");
const merge=require('webpack-merge');
const devcommon=commonfig('development');
const config=merge(devcommon,devfig);
Object.keys(config.entry).forEach((name)=>{
config.entry[name]=['webpack-hot-middleware/client?noInfo=true&reload=true'].concat(config.entry[name]);
})
const app=express();
const complier=webpack(config);
app.use(webpackDevMid(complier,{}));
app.use(webpackhot(complier,{
overlayStyles:true
}))
app.listen(8090);