本文主要跟大家分享一下如何使用webpack4来一步一步搭建自己的项目框架。如果没接触过webpack,建议在阅读本文之前先去webpack 中文网了解一下基本概念、核心理念 入口(entry)、输出(output)、loader、插件(plugins)等知识。
主要功能:
1、支持VUE
2、支持React
3、支持less、scss
4、图片压缩,css分离
5、px转rem
6、ES6
7、打包后自动压缩zip文件
8、支持本地调试
9、支持本地mock数据服务
1、首先谈一谈使用webpack搭建项目框架的目的
随着公司发展,每个项目需要的人员也随之增加,但是大家的技术栈不一样,有vue,react,angular;有人习惯原生css,有人喜欢less、scss,在项目中,为了让大家都还使用自己擅长的技术栈来开发,带着这样的目的,从而从0到1,慢慢搭建起自己的项目框架,目前框架已生成脚手架,发布到npm,有兴趣的同学可以安装体验一下。
使用步骤:
1、安装脚手架,执行 npm install light-app-cli -g
2、创建项目,执行light-app-cli <项目名称>
3、详见 [GitHub地址](https://github.com/hanhan3682523/light-app-cli)
2、webpack搭建项目框架的过程
1、生成环境及主流程
环境要求:
1、node >= 10.5.0,[node下载地址]: https://nodejs.org/en/download/
2、webpack >=4.16.0,安装:npm install -g webpack
3、框架目录结构预览
搭建流程:
1、目录及文件说明
2、配置入口(entry)
3、配置出口(output)
4、配置loader
5、配置插件(plugins)
6、开发环境配置devSever
2、目录及文件说明
|- build
-- webpack.base.conf.js--开发和打包用到的公用配置项,通过webpack-merge 分别使用
-- webpack.build.config.js--打包配置
-- webpack.dev.config.js--开发配置,生成本地服务,设置访问域名、热替换、代理、端口等信息
-- webpack.plugins.js--配置webpack插件
-- webpack.rules.js--配置webpack loader,进行文件处理
|- config
-- index.js--本地服务域名、端口等配置
|- dist
|- mock
-- data.js--设置mock接口数据
-- server.js--本地mock服务
|- src
|- assets
|- css
|- image
|- js
-- index.html
|- zip
|- .babelc
|- package.json
|- postcss.config.js
|- tsconfig.json
2、配置入口(entry)
项目为多页面应用,会涉及到多个入口,所以我们进行约定,所有入口文件统一放到src目录下的js文件夹中,利用globby模块,读取js文件夹下的所有js名称(不包括子文件夹),从而返回一个入口(entry)对象。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| //读取文件 const glob = require('globby');
let entryConfig = (function() { let _config = {}; //选择js目录下的文件,不包含common中的js作为入口 const fileList = glob.sync(['./src/js/*.*']); console.info('tag', fileList); if (fileList && fileList.length > 0) { for (let i = 0; i < fileList.length; i++) { _config[fileList[i].match(/([^\/]+)(?=\.)/ig)[0]] = fileList[i]; } } return _config; })();
//入口 entry: entryConfig,
|
3、配置出口(output)
在filename中添加了hash命名,目的是为了防止cdn缓存、浏览器缓存,每次打包,都会对静态资源重新命名,这样修改发布后,只需要推html的缓存即可。
1 2 3 4 5 6 7 8
| //出口 output: { filename: 'js/[name][hash].js', path: config.build.assetsRoot, publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath },
|
4、配置loader
项目中文件类型较多,每种文件类型都有专门的loader进行处理,涉及的loader比较多,所以把loader统一放到webpack.rules.js中进行管理,方便维护和扩展。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
| //loader,rulesConfig内容详见下面webpack.rules.js module: { rules: rulesConfig },
//webpack.rules.js 中内容
const path = require('path'); const extractTextPlugin = require("extract-text-webpack-plugin");
function resolve(dir) { return path.join(__dirname, '..', dir) }
module.exports = [{ test: /\.vue$/, use: { loader: 'vue-loader' }, //加快搜索速度 exclude: resolve('node_modules'), }, { test: /\.ts$/, use: { loader: 'ts-loader' }, //加快搜索速度 exclude: resolve('node_modules'), }, { test: /\.js$/, use: { loader: 'babel-loader', options: { presets: ['env', 'react'], plugins: ['transform-runtime'], cacheDirectory: true } }, exclude: resolve('node_modules'), }, { test: /\.css$/, use: extractTextPlugin.extract({ fallback: "style-loader", use: ["css-loader", "postcss-loader"], // css中的基础路径 publicPath: "../" }) }, { test: /\.((woff2?|svg)(\?v=[0-9]\.[0-9]\.[0-9]))|(woff2?|svg|jpe?g|png|gif|ico)$/, use: [{ loader: 'url-loader', options: { limit: 8192, //设置转换成base64的大小 name: '[name][hash:8].[ext]', outputPath: 'image/' } }], //只命中src目录中的文件,加快搜索速度 include: resolve('src') }, { test: /\.(html)$/, use: { loader: 'html-loader' }, //只命中src目录中的文件,加快搜索速度 include: resolve('src') }, { test: /\.less$/, use: extractTextPlugin.extract({ fallback: "style-loader", use: ["css-loader", "postcss-loader", "less-loader"], // css中的基础路径 publicPath: "../" }), //只命中src目录中的文件,加快搜索速度 include: resolve('src') }, { test: /\.(scss|sass)$/, // 分离的写法 use: extractTextPlugin.extract({ fallback: "style-loader", use: ["css-loader", "postcss-loader", "sass-loader"], // css中的基础路径 publicPath: "../" }), //只命中src目录中的文件,加快搜索速度 include: resolve('src') }];
|
5、配置插件(plugins)
插件可以自定义webpack的构建过程,webpack本身也内置了一些插件。同loader一样,为了便于管理,我们把插件统一放到webpack.plugins.js中进行管理,这里面使用插件主要用于分离css文件,处理vue文件,以及打包后自动压缩zip文件等。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
| //插件,pluginsConfig内容见下面webpack.plugins.js plugins: pluginsConfig
//webpack.plugins.js
//配置文件 const config = require('../config/index') //路徑 const path = require('path'); // html模板 const HtmlWebpackPlugin = require('html-webpack-plugin'); // 清除目录等 const CleanWebpackPlugin = require('clean-webpack-plugin'); // 分离css const extractTextPlugin = require("extract-text-webpack-plugin"); //静态资源输出 const copyWebpackPlugin = require("copy-webpack-plugin"); //css压缩 const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin'); //vue插件 const VueLoaderPlugin = require('vue-loader/lib/plugin'); //打包文件 const FileManagerPlugin = require('filemanager-webpack-plugin');
//读取文件 var glob = require('globby'); //webpack const webpack = require('webpack');
var pluginsConfig = [ new webpack.HotModuleReplacementPlugin(), new webpack.NamedModulesPlugin(), // 调用之前先清除 new CleanWebpackPlugin(['dist', 'zip'], { root: path.resolve(__dirname, "..") }), //vue插件 new VueLoaderPlugin(), //静态资源输出 new copyWebpackPlugin([{ from: path.resolve(__dirname, "../src/assets"), to: './assets' }]), // 分离css插件参数为提取出去的路径 new extractTextPlugin({ filename: 'style/[name][hash].css', }), //css进行压缩 new OptimizeCssAssetsPlugin({ //assetNameRegExp: /\.style\.css$/g, cssProcessor: require('cssnano'), cssProcessorOptions: { discardComments: { removeAll: true } }, canPrint: true }) ];
(function() { const fileList = glob.sync(['./src/*.html']); if (fileList && fileList.length > 0) { for (var i = 0; i < fileList.length; i++) { pluginsConfig.push( new HtmlWebpackPlugin({ template: fileList[i], filename: fileList[i].match(/([^\/]+)(?=\.)/ig)[0] + '.html', chunks: [fileList[i].match(/([^\/]+)(?=\.)/ig)[0]], hash: false, //引入的文件设置hash值 }) ); } } })();
//自动压缩dist 到zip文件 //环境判断 if (process.env && process.env.NODE_ENV && process.env.NODE_ENV.trim() === "production" && config.build.zipName) { // 调用之前先清除 pluginsConfig.push(new CleanWebpackPlugin(['zip'])); let _zipfilename = config.build.zipName; pluginsConfig.push(new FileManagerPlugin({ onEnd: { //c mkdir: ['./zip', './tempzip/' + _zipfilename], copy: [{ source: './dist', destination: './tempzip/' + _zipfilename }, ], archive: [{ source: './tempzip/', destination: './zip/' + _zipfilename + '.zip' }], delete: [ './tempzip/' ] } })); }
module.exports = pluginsConfig;
|
6、开发环境配置devSever
项目开发过程中,为了便于开发调试,我们需要设置本地开发环境,起http服务,支持自动打开浏览器,文件修改后自动刷新或热更新页面,https,接口请求代理等功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| const config = require('../config/index') const common = require('./webpack.base.conf'); const merge = require('webpack-merge'); module.exports = merge(common, { mode: 'development', //生成map文件,供调试 devtool: 'eval-source-map', //监听文件更新,在文件发生变化时重新编译,使用 DevServer 时,监听模式默认是开启的。 watch: true, //控制监听模式 watchOptions: { // 不监听的文件或文件夹,支持正则匹配 ignored: /node_modules/, // 监听到变化发生后会等300ms再去执行动作,防止文件更新太快导致重新编译频率太高,默认为 300ms aggregateTimeout: 300, // 判断文件是否发生变化是通过不停的去询问系统指定文件有没有变化实现的,默认每秒轮询1000次 poll: 1000 }, //http服务设置 devServer: { //代理 proxy: { '/api': 'http://127.0.0.1:9001', changeOrigin: true }, //运行目录 contentBase: './', //一切服务都启用gzip 压缩: compress: true, //端口号 port: config.dev.port || '8080', //自动打开浏览器 open: true, //模块热替换 hot: true, //页面自动刷新 inline: true, //打开的页面 openPage: '', //host:'0.0.0.0'--别人可以访问 host: config.dev.host || 'hxj.com', //支持https https: config.dev.https, } });
|