一、前言
最近需要为公司的活动写8个左右的移动端分享页面,有比较多的页面是公用的,如果用传统的方式来写的话,对于公用的代码抽取,css代码的压缩都是比较麻烦的,所以选择了webpack来搭建一个基本的多页面应用。大概有一下的功能点:
1.自动添加浏览器前缀到CSS文件中
2.自动压缩合并 CSS 和 JS 文件
3.自动清理打包后的dist文件夹
4.自动生成HTML文件
5.自动抽取CSS文件
6.本地开发热更新
7.HTML模板的导入等
二、目录结构
三、安装依赖
autoprefixer:自动为CSS文件添加浏览器前缀
clean-webpack-plugin:自动清除打包之后的文件
extract-text-webpack-plugin:CSS文件的单独抽取
html-loader:导入公用的HTML模板,如下使用
webpack:主要的打包工具
webpack-dev-server:本地开发的热更新服务器
webpack-merge:对webpack配置进行合并操作
hogan:前端的HTML模板
http-server:http服务器,可以用于访问打包之后的静态资源
四、具体的package.json文件
{ "name": "activityShare", "version": "1.0.0", "description": "活动分享", "main": "index.js", "scripts": { "dev": "webpack-dev-server --inline --progress --config config/webpack.config.dev.js", "build": "webpack -p --config config/webpack.config.prod.js", "serve": "http-server dist -p 9999" }, "keywords": [ "jqery、css、html" ], "author": "lidongheng", "license": "ISC", "devDependencies": { "autoprefixer": "^7.1.3", "clean-webpack-plugin": "^0.1.16", "css-loader": "^0.28.7", "extract-text-webpack-plugin": "^3.0.0", "file-loader": "^1.1.4", "html-loader": "^0.5.5", "html-webpack-plugin": "^2.30.1", "http-server": "^0.10.0", "postcss-import": "^11.0.0", "postcss-loader": "^2.0.6", "postcss-url": "^7.2.1", "style-loader": "^0.18.2", "url-loader": "^0.5.8", "webpack": "^3.5.5", "webpack-dev-server": "^2.7.1", "webpack-merge": "^4.1.0" }, "dependencies": { "hogan": "^1.0.2" } }
五、config.js文件包含一些可配置的选项
module.exports = { dev: { assetsPublicPath: '/', proxyTable: { '/api': { target: 'http://192.168.9.19:8080', changeOrigin: true, //改变源 pathRewrite: { '^/api': '' } } }, host: '0.0.0.0', // 写成0.0.0.0手机可以通过局域网访问,localhost无法使用手机访问的 port: 8888, autoOpenBrowser: false, // 是否自动打开浏览器 errorOverlay: true, // 编译错误的时候,在浏览器显示mask poll: false, }, prod: { assetsPublicPath: '/' } }
六、dev.env.js文件和prod.env.js文件,用于定义开发环境和生产环境
七、webpack基本配置文件webpack.config.base.js
const path = require("path"); // 引入插件,自动生成HTML文件的插件 const HTMLWebpackPlugin = require("html-webpack-plugin"); // 清理 dist 文件夹 const CleanWebpackPlugin = require("clean-webpack-plugin") // 抽取 css 到单独的文件 const ExtractTextPlugin = require("extract-text-webpack-plugin"); // 引入webpack const Webpack = require("webpack"); // 基本配置 const config = require("./config"); // 解析路径,当前base文件的上一级路径 function resolve (dir) { return path.join(__dirname, '..', dir) } // 计算HtmlWebpackPlugin的参数 // template是html文件的名称,title是html文件中的title值 function getHtmlWebpackPluginParams(template,title){ // 生成的HTML文件放到/dist/view目录下 return { filename: `view/${template}.html`, // 生成的文件放在/dist/view目录下 template: resolve(`src/view/${template}.html`), // 模板的位置 title: title, // 设置HTML文件中的title值 inject: true, // 插入的script标签放在body后面 hash: true, // 产生hash值 chunks: [template, 'common'], // script标签中插入的文件为对应html文件的js以及公用的common } } var configs = { // 多页应用,所以在入口处设置多个入口的JS文件 entry: { "common": ["./src/page/common/index.js"], // 公用的JS文件 "activitydetail": ["./src/page/activitydetail/index.js"], // 活动详情页面 "specialdetail": ["./src/page/specialdetail/index.js"], // 活动详情页面 "signup": ["./src/page/signup/index.js"], // 活动详情报名页面 "specialsignup": ["./src/page/specialsignup/index.js"], // 专题详情报名页面 "selectteam": ["./src/page/selectteam/index.js"], // 选择团队的页面 "createteam": ["./src/page/createteam/index.js"], // 创建团队的页面 "viewmember": ["./src/page/viewmember/index.js"], // 查看成员的页面 "download": ["./src/page/download/index.js"], // 下载页面的 }, output:{ filename: 'js/[name].[chunkhash].js', // 【chunkhash】改变内容的时候,hash值就会改变,到时候会强制浏览器获取新的文件 publicPath: process.env.NODE_ENV === 'prod' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, path: resolve('dist') // 打包的文件放到/dist下 }, externals : { 'jquery' : 'window.jQuery' // 在页面中通过script引入JQ,在page目录下对应的JS文件可以直接使用$符号 }, // 加载器 module: { rules: [ { // 对 css 后缀名进行处理 test:/.css$/, // 抽取 css 文件到单独的文件夹 use: ExtractTextPlugin.extract({ fallback: "style-loader", // 编译后用什么style-loader来提取css文件 use: [ { loader:"css-loader", options:{ minimize:true, // 开启 css 压缩 } }, { loader:"postcss-loader", } ] }) }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: 'url-loader', options: { limit: 100, name: path.posix.join('resource/img/[name].[hash:7].[ext]') } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: 'url-loader', options: { limit: 100, name: path.posix.join('resource/media/[name].[hash:7].[ext]') } }, { test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: 'url-loader', options: { limit: 100, name: path.posix.join('resource/fonts/[name].[hash:7].[ext]') } }, { test: /.string$/, loader: 'html-loader' } ], }, // 别名 resolve : { alias : { '@' : resolve('src'), node_modules : resolve('node_modules'), utils : resolve('src/utils'), page : resolve('src/page'), service : resolve('src/service'), image : resolve('src/image') } }, plugins:[ // 抽取公用的JS文件,打包到/dist/js/common.js文件中,也会把common 的chunk打包到/dist/js/common.js中 new Webpack.optimize.CommonsChunkPlugin({ name: "common", // 入口中的common chunk filename: "js/common.[hash].js" }), // 自动清理 dist 文件夹 new CleanWebpackPlugin(["dist"],{ root: resolve('/'), verbose: true, dry: false }), // 将 css 抽取到/dist/css/xxx.css new ExtractTextPlugin("css/[name].css"), // 自动生成 HTML 插件 new HTMLWebpackPlugin(getHtmlWebpackPluginParams('activitydetail','活动详情')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('specialdetail','专题详情')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('signup','填写报名单')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('specialsignup','填写报名单')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('selectteam','选择团队')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('createteam','创建团队')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('viewmember','查看成员')), new HTMLWebpackPlugin(getHtmlWebpackPluginParams('download','下载青未了')) ] } module.exports = configs;
八、本地开发的基本配置文件webpack.config.dev.js
const path = require("path"); // 引入基础配置文件 const webpackBase = require("./webpack.config.base"); // 引入 webpack-merge 插件 const webpackMerge = require("webpack-merge"); // 引入webpack const Webpack = require("webpack"); // 配置 const config = require("./config"); // 合并配置文件 module.exports = webpackMerge(webpackBase,{ // 配置 webpack-dev-server devServer:{ // 项目根目录 contentBase: path.join(__dirname, '..', 'dist'), clientLogLevel: 'warning', hot: true, // 启用 webpack 的模块热替换特性,此特性需要加载plugins:webpack.HotModuleReplacementPlugin compress: true, // 开启gzip压缩 host: config.dev.host, progress: true, port: config.dev.port, open: config.dev.autoOpenBrowser, // 不自动打开浏览器 publicPath: config.dev.assetsPublicPath, // 错误、警告展示设置 overlay: config.dev.errorOverlay ? { errors: true, warnings: false } : false, watchOptions: { poll: config.dev.poll }, // 代理 proxy: config.dev.proxyTable }, plugins: [ // DefinePlugin 允许创建一个在编译时可以配置的全局常量,可以在JS文件中根据这些定义的变量来定义不同的行为 new Webpack.DefinePlugin({ 'process.env': require("./dev.env") }), // 热加载需要配置的插件 new Webpack.HotModuleReplacementPlugin() ] })
九、生产环境打包的webpack.config.prod.js
// 引入基础配置 const webpackBase = require("./webpack.config.base"); // 引入 webpack-merge 插件 const webpackMerge = require("webpack-merge"); // 引入 webpack const webpack = require("webpack"); // 合并配置文件 module.exports = webpackMerge(webpackBase,{ plugins:[ // DefinePlugin 允许创建一个在编译时可以配置的全局常量,可以在JS文件中根据这些定义的变量来定义不同的行为 new webpack.DefinePlugin({ 'process.env': require("./prod.env") }), // 代码压缩 new webpack.optimize.UglifyJsPlugin({ // 开启 sourceMap sourceMap: true }) ] });
十、主要参考了vue-cli的配置文件结构,有webpack基础的同学应该都能看懂,把主要的配置文件贴出来,有不懂的请留言。还有一点就是没有配置ES6语法的转换,而是直接使用了webpack对js文件的打包,有需要的可以参考vue-cli进行配置。demo