内容简介:前两天帮前同事写一个兼容公安内网,外网,专网的多地图合一的地图类库,但是越写越烦躁,整理一下有以下几个痛点:所以打算使用webpack作为新轮子的打包工具。首先第一步是初始化项目。
前两天帮前同事写一个兼容公安内网,外网,专网的多地图合一的地图类库,但是越写越烦躁,整理一下有以下几个痛点:
- 使用 es5语法编写javascript,语法啰嗦冗长 。
- js代码全部写到一个文件中, 没有模块化,项目难以维护 。
- 需要 手动使用压缩 工具 压缩代码 。
所以打算使用webpack作为新轮子的打包工具。
预期目标
- 期望使用 es6语法 编写插件代码,代码整洁易读。
- 支持 模块化编程 ,项目代码划分清晰。
- 代码 合并打包和压缩自动化 。
- 支持启动 开发环境调试插件代码 。
- 插件支持
window
全局引用 和支持commonjs
模块导入
初始化项目文件
npm初始化
首先第一步是初始化项目。
输入 npm init
命令生成 package.json
文件。
创建项目文件夹
接下来是将各个模块的文件架创建完成。
├─ build # 存放webpack配置代码 ├─ config # 存放关键参数配置代码 ├─ dist # 打包后生产文件夹 ├─ example # 开发环境demo代码 ├─ src # 项目源文件 ├─ .npmignore #发布npm包时忽略文件(一般用以排除node_module文件夹) └─ package.json # 项目信息配置文件 复制代码
定义关键参数
创建js文件
在 config
目录下新建脚本文件
├─ config # 存放关键参数配置代码 | ├─ dev.env.js # 定义开发环境的环境变量 | ├─ index.js # 开发环境和生产环境的配置webpack关键参数 | ├─ prod.env.js # 定义生产环境的环境变量 复制代码
编写代码
-
prod.env.js
文件的代码。
const pkg = require('../package.json') // 引入package.json文件 // 定义环境变量和版本号 // 1. 可以使用process.env.NODE_ENV语句区分是开发环境还是生产环境 // 2. 可以使用process.env.VERSION获取当前插件版本号 module.exports = { VERSION: JSON.stringify(pkg.version), NODE_ENV: JSON.stringify('production') } 复制代码
-
prod.env.js
文件的代码。
const merge = require('webpack-merge') // 引入webpack配置合并工具 const prodEnv = require('./prod.env') // 引入生产环境的环境变量配置 // 合并prod.env和dev.env的配置 module.exports = merge(prodEnv,{ NODE_ENV: JSON.stringify('development') }) 复制代码
-
index.js
文件的代码。
const path = require('path') const { getIp } = require('../build/util') // 引入获取本机局域网内ip地址方法 // dist文件夹地址 let distPath = path.resolve(__dirname, '../dist') let config = { build:{ main: './src/index.js', // 源码入口 assetsRoot: distPath,//生产包将会被打包到/dist目录中 devtool: 'source-map' // 生成source-map文件,它为 bundle 添加了一个引用注释,以便开发工具知道在哪里可以找到它。 }, dev:{ main: './example/src/index.js', // 调试demo代码入口 assetsRoot: distPath, //开发包将会被打包到/dist目录中 assetsSubDirectory:'',//静态资源存放目录 assetsPublicPath:'/', // 公用基础路径,类似于html的base标签 devtool:'eval-source-map', host: getIp(), // WebpackDevServer 启动的IP地址 port: 8092 // WebpackDevServer 启动的端口号 } } module.exports = config 复制代码
编写开发环境webpack配置代码
创建js文件
在 build
目录下新建 webpack公用配置文件和开发环境配置文件 。
├─ build # 存放webpack配置代码 | ├─ webpack.common.js # webpack 公用基础配置 | ├─ webpack.dev.conf.js # webpack 启动开发环境入口 复制代码
编写代码
-
webpack.common.js
文件的代码。
将构建开发环境和构建生产代码的公用配置抽离出来。
const path = require('path') let srcPath = path.resolve(__dirname, '../src') // 源码文件夹路径 module.exports = { context: path.resolve(__dirname, '../'), // 基础目录,用以解析entry入口路径 resolve: { extensions: ['.js'], // 自动添加拓展名,如:import './a',会自动解析为import './a.js' alias: { '@': srcPath // 定义源码文件夹路径的别名,如:import '@',会解析为 import 'X:xx/xx/src' } }, module: { rules: [ // babel-loader,将es6转换成es5 { test: /\.js$/, include: [srcPath, path.resolve(__dirname, '../example')], loader: 'babel-loader' } ] } } 复制代码
-
webpack.dev.conf.js
文件的代码。
定义了在调试demo中启动开发环境的配置,使用 loader
对样式文件进行了处理,并做了一些调试信息的优化。
const merge = require('webpack-merge') const webpack = require('webpack') const path = require('path') const WebpackDevServer = require("webpack-dev-server") const FriendlyErrorsWebpackPlugin = require('friendly-errors-webpack-plugin') const HtmlWebpackPlugin = require('html-webpack-plugin') //设置全局环境变量 const env = require('../config/dev.env') process.env.NODE_ENV = env.NODE_ENV //引入公用配置文件 const webpackCommon = require('./webpack.common') // 引入开发环境配置参数 const config = require('../config').dev // 合并开发环境webpack配置和公用配置 let webpackDev = merge(webpackCommon, { entry: { main: config.main // 定义调试demo代码入口 }, output: { path: config.assetsRoot, // 内存中映射地址 filename: path.join(config.assetsSubDirectory, 'js/[name].js'), // 入口文件的文件名称 chunkFilename: path.join(config.assetsSubDirectory, 'js/[name].js'),// 分包加载脚本的文件名称 publicPath: config.assetsPublicPath }, devtool: config.devtool, // 生成source-map module: { rules: [ { test: /\.css$/, exclude: /node_modules/, use: ['style-loader', 'css-loader', 'postcss-loader'] }, { test: /\.(scss|sass)$/, exclude: /node_modules/, use: ['style-loader', 'css-loader', 'postcss-loader', 'sass-loader'] }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url-loader', options: { limit: 1024 * 10, name: path.join(config.assetsSubDirectory, 'img/[name].[ext]') } } ] }, plugins: [ // 定义环境变量,在自定义的插件脚本中可以获取到 new webpack.DefinePlugin({ 'process.env': env }), // 启动开发环境时,提示更友好 new FriendlyErrorsWebpackPlugin({ compilationSuccessInfo: { messages: [`Your application is running here: http://${config.host}:${config.port}`], } }), // 定义入口html文件 new HtmlWebpackPlugin({ filename: 'index.html', template: path.resolve(__dirname, `../example/index.html`), inject: true }) ] }) let compiler = webpack(webpackDev) let server = new WebpackDevServer(compiler, { quiet: true, // 除了初始启动信息之外的任何内容都不会被打印到控制台 host: config.host, // server的ip地址 port: config.port// server的端口号 }) server.listen(config.port, config.host, function () { // 启动中的提示 console.log('> Starting dev server...') }) 复制代码
开启开发环境
在 example
目录下新建js脚本,html文件和样式文件, 在这里引入插件源码,进行调试。
├─ example # 开发环境demo目录 | ├─ src # 定义开发环境的环境变量 | | ├─ index.js # demo脚本可以使用es6编写(在这里引用src的源码文件进行调试) | ├─ styles # 定义demo的样式文件 | | ├─ index.sass # demo样式可以使用sass编写 | ├─ index.html # demo的html入口文件 复制代码
这里我们看到开发环境开启成功了。
编写构建生产文件的webpack配置代码
创建js文件
在 build
目录下新建 webpack构建生产文件配置文件 。
├─ build # 存放webpack配置代码 | ├─ webpack.common.js # webpack 公用基础配置 | ├─ webpack.dev.conf.js # webpack 启动开发环境入口 | ├─ webpack.pro.conf.js # webpack 构建生产文件配置入口 (新建) 复制代码
编写代码
定义了生产文件中的环境变量,已经对生产包进行了压缩优化体积。
构建生产文件成功后会启动生产包依赖模块视图, 可根据此试图进行代码优化。
根据 output.library
的配置, 可以使用多种模块化(window, amd, commonjs)引用方式引用 。
const merge = require('webpack-merge') const webpack = require('webpack') const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin') const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin const webpackCommon = require('./webpack.common') const ora = require('ora') const chalk = require('chalk') //设置全局环境变量 const env = require('../config/prod.env') process.env.NODE_ENV = env.NODE_ENV //引入构建生产文件配置 const config = require('../config').build // 合并公用配置和构建生产文件配置 const webpackConfig = merge(webpackCommon, { entry: { main: config.main // src目录下的源码入口地址 }, output: { path: config.assetsRoot,// 生产打包后的存放的目录 filename: '[name].min.js', // 生产打包后的文件名称 library: { root: "TdrMap", // 在window对象中如何调用,如:window.TdrMap amd: "tdr-map", // 在amd规范下使用'tdr-map名称引用', 如:require(['webpackNumbers'], function(){}) commonjs: "tdr-map" // 在commonjs规范下使用'tdr-map名称引用',如 var TdrMap = require('tdr-map') }, libraryTarget: 'umd', // 将你的 library 暴露为所有的模块定义下都可运行的方式 libraryExport: "default" // 如果使用export default导出模块的话,配置为'default' }, devtool: config.devtool, plugins: [ // 定义环境变量,在自定义的插件脚本中可以获取到 new webpack.DefinePlugin({ 'process.env': env }), //如果你引入一个新的模块,会导致 module id 整体发生改变,可能会导致所有文件的chunkhash发生变化 //HashedModuleIdsPlugin根据模块的相对路径生成一个四位数的hash作为模块id,这样就算引入了新的模块,也不会影响 module id 的值 new webpack.HashedModuleIdsPlugin(), new ParallelUglifyPlugin({ // 缓存压缩后的结果,下次遇到一样的输入时直接从缓存中获取压缩后的结果返回 // cacheDir 用于配置缓存存放的目录路径 cacheDir: 'node_modules/.uglify-cache', sourceMap: true, output: { // 最紧凑的输出 beautify: false, // 删除所有的注释 comments: false }, compress: { // 在UglifyJs删除没有用到的代码时不输出警告 warnings: false, // 删除所有的 `console` 语句,可以兼容ie浏览器 drop_console: false, // 内嵌定义了但是只用到一次的变量 collapse_vars: true, // 提取出出现多次但是没有定义成变量去引用的静态值 reduce_vars: true } }), new webpack.optimize.ModuleConcatenationPlugin(),//作用域提升 (scope hoisting) // 查看 webpack 打包后所有组件与组件间的依赖关系,可以针对性的对过大的包进行优化 new BundleAnalyzerPlugin({ analyzerHost: '127.0.0.1', // 分析界面的启动url地址 analyzerPort: 8888, openAnalyzer: false }) ] }) // 构建中的提示信息 const spinner = ora('生产文件构建中...').start() spinner.color = 'green' // 开始打包构建生产文件并对打包完成对最终信息进行显示 webpack(webpackConfig, (err, stats) => { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + '\n\n') if (stats.hasErrors()) { console.log(chalk.red(' 构建失败,出现错误.\n')) process.exit(1) } console.log(chalk.cyan(' 构建完成.\n')) console.log(chalk.yellow( ' Tip: 生产文件存放在dist目录下.\n' )) }) 复制代码
编写插件代码
下面我们在 src
目录下写几句伪代码,首先先创建js文件:
├─ src # 插件源码目录 | ├─ index.js # 源码入口文件 | ├─ Map.js # 地图类文件 | ├─ decorator.js # 装饰器文件 复制代码
-
index.js
的文件代码
在入口文件导入地图类,并导出,使之可被外部调用。
import MapConstructor from './Map' // 导入地图类 export default MapConstructor 复制代码
-
Map.js
的文件代码
import MarkerConstructor from './Marker' import { addVersion } from './decorator' // 私有方法名称 const _init = Symbol('_init') /** * @class 兼容三网地图类 * @param { DOM } ele - 传入DOM对象 * @returns { Map } 返回地图的实例化对象 */ @addVersion() // 为地图类添加版本号的装饰器 export default class Map { ele map = null constructor(ele) { this.ele = ele this[_init]() } /** * 初始化地图对象的方法 * @private */ [_init]() { // 创建Map实例 this.map = new BMap.Map(this.ele) console.log('初始化map对象') } /** * 设置地图中心经纬度和层级 * @public * @param {float} lon 经度 * @param {float} lat 纬度 * @param {int} zoom 地图层级 */ centerAndZoom(lon = 116.404, lat = 39.915, zoom = 11){ this.map.centerAndZoom(new BMap.Point(lon, lat), zoom); // 初始化地图,设置中心点坐标和地图级别 } } 复制代码
-
decorator.js
的文件代码
process.env.VERSION
的值是在 webpack.pro.conf.js
中的 webpack.DefinePlugin
插件导入的。
/** * 为class添加版本信息的装饰器 * @returns { Function } 返回装饰器方法 */ export const addVersion = () => { return function (target){ if (typeof target !== 'function') throw new Error('this is not a constructor') // process.env.VERSION 是webpack注入的插件版本信息 target.prototype.version = process.env.VERSION } } 复制代码
打包生产文件
使用 npm run build
就可以将打包后的代码存放到 dist
目录下。
如何引用插件
- 使用
<script>
标签引入打包后的main.min.js
文件可以直接在window
对象下面直接实例化TdrMap
类。
<script src="./main.min.js"></script> 复制代码
- 使用
commonjs
模块规范引入的脚本。(我们必须先在package.json
文件下定义main
属性的值为dist/main.min.js
,表示这个插件的入口文件是main.min.js
文件)
import TdrMap from 'tdr-map' 复制代码
测试打包后的生产文件是否能调用成功
最后看到TdrMap类已经可以成功实例化了,插件的版本信息也成功打印出来了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。