WdBly Blog

懂事、有趣、保持理智

周维的个人Blog

懂事、有趣、保持理智

站点概览

周维 | Jim

603927378@qq.com

推荐阅读

webpack 配置分析

从应用的入口到应用的打包配置,再到插件的应用分析webpack的关键配置

image.png

开始一个项目

首先确保安装最新版nodejs
通过npm init 生成package.json文件(此处需要注意确保目录没有中文)
否则提示:

image.png
初始package.json文件如下

{ "name": "test", "version": "1.0.0", "description": "test package", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1" }, "repository": { "type": "git", "url": "none" }, "author": "WdBly", "license": "ISC" }

npm 允许在package.json文件里面,使用scripts字段定义脚本命令。
在当前目录下执行 npm test或者 npm run test执行脚本,相当于直接在命令行执行了:echo “Error: no test specified” && exit 1
详情的npm script教程请参考阮一峰老师的文章


构建项目目录和package.json

项目目录如下

image.png

其中build和config文件夹是分析的重点,webpack的配置我们都会放到这两个文件夹中。

除了package.json文件我发现还有一个package-lock.json文件,这个文件的功能是锁定项目依赖的结构(node_modules目录下依赖的结构),是为了解决团队开发时,由于不同的成员的本地系统,环境,或者是npm的版本的不一致导致的错误而做的兼容。
npm参考文章

package.json内容如下:

{ "name": "bi_front", "version": "1.0.0", "description": "BI", "main": "index.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", "dev": "cross-env NODE_ENV=development webpack-dev-server --config build/webpack.config.js --hot", "build": "cross-env NODE_ENV=production webpack --config build/webpack.config.js --progress", "server": "node server" }, "repository": { "type": "git", "url": "" }, "keywords": [], "author": "", "license": "ISC", "dependencies": { ... }, "devDependencies": { ... "cross-env": "^5.1.1", ... } }

自定义的scripts中新增了dev命令:
cross-env NODE_ENV=development 代表当前环境为开发环境,NODE_ENV可以通过process.env.NODE_ENV全局访问

使用cross-env 必须先引入相应的包:npm i cross-env -D

webpack-dev-server --config build/webpack.config.js
webpack-dev-server:此命令用于启动express的Http服务,使用的websocket协议,达到实时编译的目的。
–config build/webpack.config.js:
使用另一份配置文件(build/webpack.config.js)
–hot:热更新
–progress:显示进度条

通过运行 npm run ** 执行上面的脚本命令。


webpack.config.js

build目录 和 config目录:

image.png

image.png

webpack.config.js

var webpack = require('webpack'); var path = require('path'); var webpackMerge = require('webpack-merge'); var HtmlWebpackPlugin = require('html-webpack-plugin'); var CopyWebpackPlugin = require('copy-webpack-plugin'); var ROOT_PATH = path.resolve(__dirname, '../'); var SRC_PATH = path.resolve(ROOT_PATH, 'src'); var NODE_MODULES_PATH = path.resolve(ROOT_PATH, 'node_modules'); var additionalConfigPath = { development: './webpack.dev.config.js', production: './webpack.prod.config.js' }; var baseConfig = { entry: { index: path.resolve(SRC_PATH, 'index.js'), vendor: ['react-router', 'react', 'react-dom'] }, output: { publicPath: '/', filename: '[name].bundle.js', chunkFilename: '[name].[chunkHash:6].bundle.js' }, externals: { moment: 'moment', 'moment/locale/zh-cn': 'moment.locale' }, resolve: { modules: [NODE_MODULES_PATH], extensions: ['.js', '.webpack.js', '.web.js', '.less', '.json'] }, module: { rules: [ { test: /\.jsx?$/, exclude: /node_modules/, use: [ {loader: 'babel-loader'} ] } ] }, plugins: [ new CopyWebpackPlugin([ {from: path.resolve(SRC_PATH, 'img'), to: 'img'} ]), new HtmlWebpackPlugin({ favicon: path.resolve(SRC_PATH, 'favicon.ico'), template: path.resolve(SRC_PATH, 'index.html'), //source chunks: ['index', 'vendor'], hash: true }) ] }; module.exports = webpackMerge( baseConfig, require(additionalConfigPath[process.env.NODE_ENV]) ); //多入口配置,将第三方库打包为一个文件 entry: { index: path.resolve(SRC_PATH, 'index.js'), vendor: ['react-router', 'react', 'react-dom'] } //chunkFilename:异步加载时的文件打包处理 output: { publicPath: '/', filename: '[name].bundle.js', chunkFilename: '[name].[chunkHash:6].bundle.js' } CopyWebpackPlugin //静态资源拷贝插件 拷贝到(to)打包的更目录 HtmlWebpackPlugin //自动生成了index.html文件并且引入相关的css,js。 //chunks:选项的作用主要是针对多入口(entry)文件。当你有多个入口文件的 //时候,对应就会生成多个编译后的 js 文件。那么 chunks 选项就可以决定 //是否都使用这些生成的 js 文件。 webpackMerge //合并webpack 的配置文件,根据process.env.NODE_ENV的值 //不同而合并不同的配置文件。

开发环境中:会导入webpack.dev.config.js。

var webpack = require('webpack'); var path = require('path'); var fs = require("fs"); var ExtractTextPlugin = require('extract-text-webpack-plugin'); var ROOT_PATH = path.resolve(__dirname, '../'); var SRC_PATH = path.resolve(ROOT_PATH, 'src'); var DEV_PATH = path.resolve(ROOT_PATH, 'dev'); var DEV_GLOBAL_CONFIG = require('../config/dev.env.js'); const pkgPath = path.join(ROOT_PATH, 'package.json'); const pkg = fs.existsSync(pkgPath) ? require(pkgPath) : {}; var theme = {}; if (pkg.theme && typeof(pkg.theme) === 'string') { let cfgPath = pkg.theme; // relative path if (cfgPath.charAt(0) === '.') { cfgPath = resolve(args.cwd, cfgPath); } const getThemeConfig = require(cfgPath); theme = getThemeConfig(); } else if (pkg.theme && typeof(pkg.theme) === 'object') { theme = pkg.theme; } module.exports = { devtool: "cheap-module-eval-source-map", devServer: { hot: true, inline: true, progress: true, contentBase: DEV_PATH, historyApiFallback: true, disableHostCheck: true, compress: true, open: true, overlay: true }, output: { path: DEV_PATH }, module: { rules: [ { test: /\.less$/, use: ExtractTextPlugin.extract({ fallback: "style-loader", use: ['css-loader', { loader: 'less-loader', options: { modifyVars: theme } }] }) }, { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] }, plugins: [ new webpack.DefinePlugin(DEV_GLOBAL_CONFIG), new ExtractTextPlugin('[name].bundle.css') ] }; ExtractTextPlugin //css文件打包 DefinePlugin //比较有趣,用户给当前环境配置一组全局变量,常用来配置 //beseURL,通常我们的开发环境和发布环境的接口地址是不一样的,如果将 //beseURL写入代码中,如果打包时忘记改就很麻烦,所有推荐使用这种方式 //dev.env.js内容如下 全局配置 module.exports = { 'process.env': { NODE_ENV: JSON.stringify(process.env.NODE_ENV || 'development') }, API_ROOT: '"http://192.168.199.237:5003"' //ado //API_ROOT: '"http://192.168.100.250:5003"' //qa1 // API_ROOT: '"http://118.122.119.107:5003"' //product };

打包前指定生成目录

output: { publicPath: '/', filename: '[name].bundle.js', chunkFilename: '/js/[name].[chunkHash:6].bundle.js' },

可以在dist目录下生成js文件夹 将打包的文件放入其中
正常情况下chunkFilename被打包后的 [name]值是找不到的,替代的是 [id]
效果如下:

image.png

这样的效果就是不直观,我们需要将name显示可以使用到 require.ensure方法

使用前:

const Home = ()=> import('@/components/home/Home.vue'); const Login = ()=> import('@/components/login/Login.vue'); const AboutMe = ()=> import('@/components/aboutMe/AboutMe.vue');

使用后:

const Home = (resolve) => { require.ensure([], function(){ resolve(require('@/components/home/Home.vue')); }, 'Home'); }; //require.ensure 的第三个参数用于指定模块的路径和name

打包效果:

image.png

分文件打包处理:当我们异步加载的组件过多,项目过大时,我们希望不是所有的组件都打包到一个文件夹中,这个需求在chunkFilename配置无法达到效果,这时我们需要require.ensure的第三个参数的另一个特性指定打包生成文件路径
使用前:

image.png

修改router.js

const Home = (resolve) => { require.ensure([], function(){ resolve(require('@/components/Home.vue')); }, 'home/Home'); }; const Login = (resolve) => { require.ensure([], function(){ resolve(require('@/components/Login.vue')); }, 'login/Login'); }; // home/Home login/Login

再次打包:

image.png

home文件夹中:

image.png

当然如果你不想使用require.ensure 还有一种更加简单的办法处理:(vue-router) 使用 命名 chunk,一个特殊的注释语法来提供 chunk name;
路由懒加载

const Main = ()=> import(/* webpackChunkName: "main/Main" */ '@/components/Main.vue'); //main/Main chunk name

打包效果如上!!!

提交

全部评论0

暂时没有评论...