配置webpack作为你新轮子的打包工具

栏目: 编程工具 · 发布时间: 5年前

内容简介:前两天帮前同事写一个兼容公安内网,外网,专网的多地图合一的地图类库,但是越写越烦躁,整理一下有以下几个痛点:所以打算使用webpack作为新轮子的打包工具。首先第一步是初始化项目。

前两天帮前同事写一个兼容公安内网,外网,专网的多地图合一的地图类库,但是越写越烦躁,整理一下有以下几个痛点:

  1. 使用 es5语法编写javascript,语法啰嗦冗长
  2. js代码全部写到一个文件中, 没有模块化,项目难以维护
  3. 需要 手动使用压缩 工具 压缩代码

所以打算使用webpack作为新轮子的打包工具。

预期目标

  1. 期望使用 es6语法 编写插件代码,代码整洁易读。
  2. 支持 模块化编程 ,项目代码划分清晰。
  3. 代码 合并打包和压缩自动化
  4. 支持启动 开发环境调试插件代码
  5. 插件支持 window 全局引用 和支持 commonjs 模块导入

初始化项目文件

npm初始化

首先第一步是初始化项目。

输入 npm init 命令生成 package.json 文件。

配置webpack作为你新轮子的打包工具

创建项目文件夹

接下来是将各个模块的文件架创建完成。

├─ 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 # 定义生产环境的环境变量
复制代码

编写代码

  1. 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')
}

复制代码
  1. 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')
})

复制代码
  1. 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 启动开发环境入口
复制代码

编写代码

  1. 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'
            }
        ]
    }
}
复制代码
  1. 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作为你新轮子的打包工具

编写构建生产文件的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 # 装饰器文件
复制代码
  1. index.js 的文件代码

在入口文件导入地图类,并导出,使之可被外部调用。

import MapConstructor from './Map' // 导入地图类
export default MapConstructor
复制代码
  1. 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);  // 初始化地图,设置中心点坐标和地图级别
    }
}
复制代码
  1. 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 目录下。

配置webpack作为你新轮子的打包工具

如何引用插件

  1. 使用 <script> 标签引入打包后的 main.min.js 文件可以直接在 window 对象下面直接实例化 TdrMap 类。
<script src="./main.min.js"></script>
复制代码
  1. 使用 commonjs 模块规范引入的脚本。(我们必须先在 package.json 文件下定义 main 属性的值为 dist/main.min.js ,表示这个插件的入口文件是 main.min.js 文件)
import TdrMap from 'tdr-map'
复制代码

测试打包后的生产文件是否能调用成功

最后看到TdrMap类已经可以成功实例化了,插件的版本信息也成功打印出来了。

配置webpack作为你新轮子的打包工具

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Hackers

Hackers

Steven Levy / O'Reilly Media / 2010-5-30 / USD 21.99

This 25th anniversary edition of Steven Levy's classic book traces the exploits of the computer revolution's original hackers -- those brilliant and eccentric nerds from the late 1950s through the ear......一起来看看 《Hackers》 这本书的介绍吧!

URL 编码/解码
URL 编码/解码

URL 编码/解码

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具