摘要:配置文件的编写在目录下。当然可以根据其他的解析搭建不同开发环境,都是很容易的。
前言
自从webpack 诞生,就开启了webpack的时代,从其他的老大哥打包工具过度而来,详情可看: https://github.com/tstrilogy/...
0. 资源list of loader: https://webpack.github.io/doc... (关于webpack 所有资源的loader 列表)
webpack doc: https://doc.webpack-china.org/ (webpack的中文文档)
本机安装环境:
系统: macos nodejs: nodejs8.5 npm: 5.3
上面是系统的搭建开发模板的系统环境。
2. 配置目录及文件{ "name": "handlerbase-template", "version": "1.0.0", "description": "use handlebars", "author": "longfan.zheng", "private": true, "scripts": { "dev": "node build/dev.server.js", "build": "node build/build.js" }, "dependencies": { "bootstrap": "^3.3.7", "jquery": "^3.2.1" }, "devDependencies": { "autoprefixer": "^6.7.2", "babel-core": "^6.22.1", "babel-loader": "^6.2.10", "babel-plugin-transform-runtime": "^6.22.0", "babel-preset-env": "^1.2.1", "babel-preset-stage-2": "^6.22.0", "babel-register": "^6.22.0", "chalk": "^1.1.3", "compression-webpack-plugin": "^1.0.0", "connect-history-api-fallback": "^1.3.0", "copy-webpack-plugin": "^4.0.1", "css-loader": "^0.26.1", "eslint-friendly-formatter": "^3.0.0", "eventsource-polyfill": "^0.9.6", "express": "^4.14.1", "extract-text-webpack-plugin": "^2.0.0", "file-loader": "^0.10.0", "friendly-errors-webpack-plugin": "^1.1.3", "function-bind": "^1.1.0", "handlebars": "^4.0.10", "handlebars-loader": "^1.6.0", "handlebars-template-loader": "^0.8.0", "html-webpack-plugin": "^2.28.0", "http-proxy-middleware": "^0.17.3", "node-sass": "^4.5.3", "opn": "^4.0.2", "optimize-css-assets-webpack-plugin": "^1.3.0", "ora": "^1.1.0", "rimraf": "^2.6.0", "sass-loader": "^6.0.6", "semver": "^5.3.0", "style-loader": "^0.18.2", "url-loader": "^0.5.7", "webpack": "^2.2.1", "webpack-bundle-analyzer": "^2.9.0", "webpack-dev-middleware": "^1.10.0", "webpack-hot-middleware": "^2.16.1", "webpack-merge": "^2.6.1" }, "engines": { "node": ">= 4.0.0", "npm": ">= 3.0.0" }, "browserslist": [ "> 1%", "last 2 versions", "not ie <= 8" ] }
上面是关于开发环境的package.json的基本内容,关于依赖就是jquery,bootstrap.其他的开发依赖等待稍后配置说明.
3. 详细配置 根据package.json进行安装:$ npm install
或者
$ yarn
安装好配置准备如下目录:
目录说明以及文件说明:|- build 脚本文件位置 |- config 配置文件 |- dist 打包后的文件 |- node_modules node模块 |- src 源码文件 |- static 静态资源(这个是直接复制到打包目录的) |- .gitignore git忽略文件目录 |- home.html 普通html文件 |- home.js html入口js文件 |- index.html 同上 |- index.js 同上 |- package.json |- package-lock.json |- readme.md
文件目录配置如上说明如上。
配置文件的编写 在config 目录下。 公共配置文件 index.js文件const path = require("path") const fs = require("fs") let htmls = fs.readdirSync(path.resolve(__dirname, "..")) .filter(cv => cv.endsWith(".html")) // 得到root目录下的所有html文件 const suffix = ".vm" // 定义打包后的文件后缀 const files = {} // 定义输出文件的对象 htmls = htmls.map(function (html) { const name = html.split(".")[0] files[name] = path.resolve(__dirname, "../dist", name + suffix) }) // 得到输出文件的名称以及路径 module.exports = { build: Object.assign({ env: require("./prod.env"), assetsRoot: path.resolve(__dirname, "../dist"), assetsSubDirectory: "static", // 定义静态资源的子目录 assetsPublicPath: "/", // 定义资源的路径 productionSourceMap: true, // 定义产品环境开启sourceMap productionGzip: true, // 开启产品环境gzip 压缩 productionGizpExtensions: ["js", "css"], // 定义产品环境gzip压缩的后缀 bundleAnlyzerReport: false //关闭打包后自动打开打包分析报告 }, files), // 打包文件的内容 dev: { env: require("./dev.env"), port: 8080, // 定义开发服务器的端口 autoOpenBrowser: true, // 定义是否自动打开浏览器 assetsSubDirectory: "static", // 定义资源子目录和build的key 定义一样 assetsPublicPath: "/", // 同上 proxyTable: {}, // 代理配置 cssSourceMap: false // 关闭cssSourceMap } // 开发配置项 }
开发文件dev.env.js和prod.env.js 编写说明: 最后打包出来的文件需要使用volcityjs模板引擎来进行服务器渲染,so 打包的文件最后的即为是xxx.vm文件。需要当前的nodejs 环境支持array.filter方法以及array.map方法
dev.env.js
const merge = require("webpack-merge") // webpack 配置文件合并的模块 const proEnv = require("./prod.env") // 请求产品环境的环境变量 module.exports = merge(proEnv, { NODE_ENV: ""development"" }) // 覆盖NODE_ENV的值
prod.env.js
module.exports = { NODE_ENV: ""production"" } // 定义产品环境的NODE_ENV
上面就就定义好了开发的配置文件,主要是关于打包配置项以及开发配置项的定义。
开始编写build目录下的打包脚本以及webpack的配置文件。 webpack 配置文件webpack.base.conf.js (基本的webpack 配置文件)
const path = require("path") const config = require("../config") // 获取index.js const fs = require("fs") const webpack = require("webpack") function resolve (dir) { return path.join(__dirname, "..", dir) } // 解析根目录的方法,返回一个绝对路径 function assetsPath (_path) { var assetsSubDirectory = process.env.NODE_ENV === "production" ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } // 通过当前NODE_ENV得到资源路径 const prefix = "./" // 定义路径浅醉 let jsFiles = fs.readdirSync(path.resolve(__dirname, "..")) .filter(function (cv) { return cv.endsWith(".js") }) // 得到入口文件,在根目录的js的文件,该项目中就是home.js和index.js let entries = {} jsFiles.map(function (cv) { const name = cv.split(".")[0] entries[name] = prefix + cv return cv }) // 得到一个{ "home": "home.js", ...}这样的对象 module.exports = { entry: entries, // 配置入口文件 output: { path: config.build.assetsRoot, filename: "[name].js", publicPath: process.env.NODE_ENV === "production" ? config.build.assetsPublicPath : config.dev.assetsPublicPath }, // 配置输出 resolve: { extensions: [".js", ".hbs", ".json"], alias: {} }, // 配置require.resolve的解析,可以直接使用require("main.js")或者require("main.hbs")或者require("main.json")都可以省略后缀 devtool: "source-map", module: { rules: [ // 配置loader { test: /.js$/, loader: "eslint-loader", enforce: "pre", include: [resolve("src")], options: { formatter: require("eslint-friendly-formatter") } }, { test: /.hbs$/, // 在这里配置了hbs后缀的文件解析。 loader: "handlebars-loader", query: {} }, { test: /.js$/, loader: "babel-loader", include: [resolve("src")] }, { test: /.(png|jpe?g|gif|svg)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: assetsPath("img/[name].[hash:7].[ext]") } }, { test: /.(mp4|webm|ogg|mp3|wav|flac|aac)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: assetsPath("media/[name].[hash:7].[ext]") } }, { test: /.(woff2?|eot|ttf|otf)(?.*)?$/, loader: "url-loader", options: { limit: 10000, name: assetsPath("fonts/[name].[hash:7].[ext]") } }, { test: /.(css|scss)$/, // 这里配置sass的解析。 use: [ { loader: "style-loader" }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "sass-loader", options: { sourceMap: true } } ] } ] }, plugins: [ new webpack.ProvidePlugin({ $: "jquery", jQuery: "jquery" }) ] // 这里定义了jquery可以直接在整个项目中使用$或者jQuery,不需要定义 }
webpack.dev.conf.js
const webpack = require("webpack") const path = require("path") const merge = require("webpack-merge") const config = require("../config") const baseWebpackConfig = require("./webpack.base.conf") // 导入base const HtmlWebpackPlugin = require("html-webpack-plugin") // 导入html 解析的webpack的插件 const FriendlyErrorsPlugin = require("friendly-errors-webpack-plugin") // 导入友好的错误提示的插件 const fs = require("fs") Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ["./build/dev.client"].concat( baseWebpackConfig.entry[name]) }) // 自动读取在根目录下的html文件,并使用webpack的插件进行解析 let htmls = fs.readdirSync(path.resolve(__dirname, "..")) .filter(cv => cv.endsWith(".html")) const prefix = "./" htmls = htmls.map(function (html) { return new HtmlWebpackPlugin({ filename: prefix + html, template: prefix + html, inject: true, chunks: [html.split(".")[0]] }) }) // 得到一个HtmlWebpackPlugin 的对象,具体配置看webpack-html-plugin主页 module.exports = merge(baseWebpackConfig, { devtool: "#cheap-module-eval-source-map", plugins: [ new webpack.DefinePlugin({ "process.env": config.dev.env }), new webpack.HotModuleReplacementPlugin(), new webpack.NoEmitOnErrorsPlugin(), new FriendlyErrorsPlugin() ].concat(htmls) }) // 通过webpack-merge 合并配置文件
webpack.prod.conf.js
const path = require("path") const webpack = require("webpack") const config = require("../config") const merge = require("webpack-merge") const baseWebpackConfig = require("./webpack.base.conf") const CopyWebpackPlugin = require("copy-webpack-plugin") const HtmlWebpackPlugin = require("html-webpack-plugin") const ExtractTextPlugin = require("extract-text-webpack-plugin") const OptimizeCssPlugin = require("optimize-css-assets-webpack-plugin") const fs = require("fs") const env = process.env.NODE_ENV === "testing" ? require("../config/test.env") : config.build.env function assetsPath (_path) { var assetsSubDirectory = process.env.NODE_ENV === "production" ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } let htmls = fs.readdirSync(path.resolve(__dirname, "..")) .filter(cv => cv.endsWith(".html")) const prefix = "./" htmls = htmls.map(function (html) { const name = html.split(".")[0] return new HtmlWebpackPlugin({ filename: config.build[name], template: prefix + html, inject: true, chunks: [name, "vendor", "manifest"], minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true }, chunksSortMode: "dependency" }) }) const webpackConfig = merge(baseWebpackConfig, { devtool: config.build.productionSourceMap ? "#source-map" : false, output: { path: config.build.assetsRoot, filename: assetsPath("js/[name].[chunkhash].js"), chunkFilename: assetsPath("js/[id].[chunkhash].js") }, plugins: [ new webpack.DefinePlugin({ "process.env": env }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false }, sourceMap: true }), new webpack.optimize.CommonsChunkPlugin({ name: "vendor", minChunks: function (module, count) { return ( module.resource && /.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, "../node_modules") ) === 0) } }), new webpack.optimize.CommonsChunkPlugin({ name: "manifest", chunks: ["vendor"] }), new CopyWebpackPlugin([ { from: path.resolve(__dirname, "../static"), to: config.build.assetsSubDirectory, ignore: [".*"] } ]) ].concat(htmls) }) if (config.build.productionGzip) { const CompressionWebpackPlugin = require("compression-webpack-plugin") webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: "[path].gz[query]", algorithm: "gzip", test: new RegExp(( ".(" + config.build.productionGizpExtensions.join("|") + ")$" )), threshold: 10240, minRatio: 0.8 }) ) } if (config.build.bundleAnlyzerReport) { const BundleAnalyzerPlugin = require( "webpack-bundle-analyzer").BundleAnalyzerPlugin webpackConfig.plugins.push(new BundleAnalyzerPlugin()) } module.exports = webpackConfig
开发环境的打包文件主要是开启压缩功能,可以将node_modules的文件打包一个文件,以及开启压缩等等.
编写开发服务器脚本
const config = require("../config") if (!process.env.NODE_ENV) { process.env.NODE_ENV = JSON.parse(config.dev.env.NODE_ENV) } const opn = require("opn") const path = require("path") const express = require("express") const webpack = require("webpack") const proxyMiddleware = require("http-proxy-middleware") const webpackConfig = process.env.NODE_ENV === "testing" ? require( "./webpack.prod.conf") : require("./webpack.dev.conf") const port = process.env.PORT || config.dev.port const autoOpenBrowser = !!config.dev.autoOpenBrowser const proxyTable = config.dev.proxyTable const app = express() const complier = webpack(webpackConfig) const devMiddleware = require("webpack-dev-middleware")(complier, { publicPath: webpackConfig.output.publicPath, quiet: true, noInfo: false }) // 开启开发中间件 const hotMiddleware = require("webpack-hot-middleware")(complier, { log: () => console.log, heartbeat: 2000 }) // 开启热更新 complier.plugin("compilation", function (compilation) { compilation.plugin("html-webpack-plugin-after-emit", function (data, cb) { hotMiddleware.publish({action: "reloadd"}) cb() }) }) Object.keys(proxyTable).forEach(function (context) { const options = proxyTable[context] if (typeof options === "string") { options = {target: options} } app.use(proxyMiddleware(options.filter || context, options)) }) app.use(require("connect-history-api-fallback")()) // 拦截get请求 app.use(devMiddleware) app.use(hotMiddleware) const staticPath = path.posix.join(config.dev.assetsPublicPath, config.dev.assetsSubDirectory) app.use(staticPath, express.static("./static")) // 设置静态资源目录和上诉config目录中配置一样 const uri = "http://localhost:" + port let _resolve const readyPromise = new Promise(resolve => { _resolve = resolve }) console.log("> Starting dev server...") devMiddleware.waitUntilValid(() => { console.log("> Listening at " + uri + " ") if (autoOpenBrowser && process.env.NODE_ENV !== "test") { opn(uri) } _resolve() }) const server = app.listen(port) module.exports = { ready: readyPromise, close: () => { server.close() } }
编写客户端热更新模块:
require("eventsource-polyfill") const hotClient = require( "webpack-hot-middleware/client?noInfo=true&reload=true") hotClient.subscribe(function (event) { // console.log("[hot client]", event.action) if (event.action === "reload") { window.loaction.reload() } })
这里主要是使用webpack-hot-middleware的语法,然后根据监听时间刷新页面
编写打包脚本build.js
process.env.NODE_ENV = "production" const ora = require("ora") // 控制台的等待的控制器 const rm = require("rimraf") // 删除文件 const path = require("path") const chalk = require("chalk") // 控制台有颜色输出 const webpack = require("webpack") const config = require("../config") const webpackConfig = require("./webpack.prod.conf") const spinner = ora("building for production...") spinner.start() rm(path.join(config.build.assetsRoot, config.build.assetsSubDirectory), function (err) { if (err) throw err webpack(webpackConfig, function (err, stats) { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + " ") console.log(chalk.cyan(" Build complete. ")) console.log(chalk.yellow( " Tip: built files are meant to be served over an HTTP server. " + " Opening index.html over file:// won"t work. " )) }) })
详细请看源码注释以及文件后面的备注. 源码请移步: https://github.com/tstrilogy/...(欢迎提issue和点star)
4. 完成效果打包后生成的文件:
显示效果:
都可以写出hbs文件重复使用.
5. 扩展webpack是一个强大的打包工具,上面的配置文件是依据vue-webpack的配置文件,去除了vue的,改写成一个仅仅使用handlebars和jquery以及bootstrap的开发模板。当然可以根据其他的解析loader搭建不同开发环境,都是很容易的。
本文完.
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/88899.html
摘要:前言这里筑梦师是一名正在努力学习的开发工程师目前致力于全栈方向的学习希望可以和大家一起交流技术共同进步用简书记录下自己的学习历程个人学习方法分享本文目录更新说明目录学习方法学习态度全栈开发学习路线很长知识拓展很长在这里收取很多人的建议以后决 前言 这里筑梦师,是一名正在努力学习的iOS开发工程师,目前致力于全栈方向的学习,希望可以和大家一起交流技术,共同进步,用简书记录下自己的学习历程...
阅读 2396·2019-08-30 15:52
阅读 2208·2019-08-30 12:51
阅读 2793·2019-08-29 18:41
阅读 2783·2019-08-29 17:04
阅读 768·2019-08-29 15:11
阅读 1636·2019-08-28 18:02
阅读 3560·2019-08-26 10:22
阅读 2478·2019-08-26 10:12