webpack初步了解

webpack初步了解

构建工具

构建工具的功能

  • 代码转换:TypeScript 编译成 JavaScript、SCSS 编译成 CSS 等。
  • 文件优化:压缩 JavaScript、CSS、HTML 代码,压缩合并图片等。
  • 模块合并:在采用模块化的项目里会有很多个模块和文件,需要构建功能把模块分类合并成一个文件。
  • 代码分割:提取多个页面的公共代码、提取首屏不需要执行部分的代码让其异步加载。
  • 自动刷新:监听本地源代码的变化,自动重新构建、刷新浏览器。
  • 代码校验:在代码被提交到仓库前需要校验代码是否符合规范,以及单元测试是否通过。

常用的构建工具

  • grunt
  • gulp
  • fis3(百度)
  • webpack模块化管理工具,可以对模块进行:
    • 压缩
    • 预处理
    • 按需打包
    • 按需加载
    • 热加载

webpack介绍

  • webpack可以看做是模块打包机,它做的事情是:
    • 分析你的项目结构,找到JavaScript模块;
    • 其它的一些浏览器不能直接运行的拓展语言(Scss,TypeScript等);
    • 并将其转换和打包为合适的格式供浏览器使用;
  • Webpack和Grunt以及Gulp相比有什么特性
    • Webpack和另外两个并没有太多的可比性;
    • Gulp/Grunt是一种能够优化前端的开发流程的工具,而WebPack是一种模块化的解决方案;
    • Webpack的优点使得Webpack在很多场景下可以替代Gulp/Grunt类的工具;
    • Grunt和Gulp的工作方式是:在一个配置文件中,指明对某些文件进行类似编译,组合,压缩等任务的具体步骤,工具之后可以自动替你完成这些任务。
    • Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
  • webpack特征
    • 插件化:webpack本身非常灵活,提供了丰富的插件接口。基于这些接口,webpack开发了很多插件作为内置功能
    • 速度快:webpack使用异步IO以及多级缓存机制。所以webpack的速度是很快的,尤其是增量更新。
    • 丰富的Loaders:loaders用来对文件做预处理。这样webpack就可以打包任何静态文件。
    • 高适配性:webpack同时支持AMD/CommonJs/ES6模块方案。webpack会静态解析你的代码,自动帮你管理他们的依赖关系。此外,webpack对第三方库的兼容性很好。
    • 代码拆分:webpack可以将你的代码分片,从而实现按需打包。这种机制可以保证页面只加载需要的JS代码,减少首次请求的时间。
    • 优化:webpack提供了很多优化机制来减少打包输出的文件大小,不仅如此,它还提供了hash机制,来解决浏览器缓存问题。
    • 开发模式友好:webpack为开发模式也提供了很多辅助功能。比如SourceMap、热更新等。
    • 使用场景多:webpack不仅适用于web应用场景,也适用于Webworkers、Node.js场景

webpack命令

  • webpack 执行一次开发时的编译
  • webpack -p 执行一次生成环境的编译(压缩)
  • webpack –watch 在开发时持续监控增量编译(很快)
  • webpack -d 让他生成SourceMaps
  • webpack –progress 显示编译进度
  • webpack –colors 显示静态资源的颜色
  • webpack –display-chunks 展示编译后的分块
  • webpack –display-modules 列出打包模块
  • webpack –display-reasons 显示更多引用模块原因
  • webapck –display-error-details 显示更多报错信息

webpack常用配置介绍

出入口 Entry&Output

  • entry:入口
    • 关联的很多其他需要打包的文件
  • output:出口
    • path:文件打出的路径,这个path是nodejs内置的方法;
      entry和output配置

entry配置

  • entry是指需要打包的文件;
  • entry有几种使用方法:
    • 字符串
entry:__dirname+'/src/script/main.js'
//对应
output:{
  path:__dirname+'/dist/js',
  filename:'bundle.js'
}
  • 数组
entry:[
  __dirname+'/src/script/main.js',
  __dirname+'/src/script/a.js'
]
//对应
output:{
  path:__dirname+'/dist/js',
  filename:'bundle.js'
}
  • 对象
entry:{
        main:__dirname+'/src/script/main.js',
        a:__dirname+'/src/script/a.js'
    }
//对应
output:{
  path:__dirname+'/dist/js',
  filename:'[name]-[hash].js'
}
//or 
output:{path:__dirname+'/dist/js',filename:'[name]-[chunkhash].js'}

output的配置

  • output是指打包生成的文件
  • entry中输入多个chunk时,为确保文件名唯一避免相互覆盖使用占位符命名filename;
  • 三种占位符
    • [name]是chunk的name;
    • [hash]是本次打包的hash值,hash值相同;
    • [chunkhash]是每个chunk的hash值,不同文件同次打包不相同,保证文件的唯一性,只有改变文件中的内容时hash才变化,未做改变的文件hash值不变;

模块 Module

  • module:模块,在 Webpack眼里一切皆模块,默认只识别js文件, 如果是其它类型文件利用对应的loader转换为js模块。

    • rules:做很多的规定,比如:加载css、将es6编译成es5;
    • “-loader”其实是可以省略不写的,多个loader之间用“!”连接起来
    module: {
       //加载器配置
       loaders: [
           //.css 文件使用 style-loader 和 css-loader 来处理
           { test: /\.css$/, loader: 'style-loader!css-loader' },
           //.js 文件使用 jsx-loader 来编译处理
           { test: /\.js$/, loader: 'jsx-loader?harmony' },
           //.scss 文件使用 style-loader、css-loader 和 sass-loader 来编译处理
           { test: /\.scss$/, loader: 'style!css!sass?sourceMap'},
           //图片文件使用 url-loader 来处理,小于8kb的直接转为base64
           { test: /\.(png|jpg)$/, loader: 'url-loader?limit=8192'}
       ]
    }
    • webpack本身只能加载js模块,如果需要加载其他类型的文件(模块),就需要使用对应的loader进行转换/加载;
    • 如果我们想要在js文件中通过require引入模块,比如css或image,那么就需要在这里配置加载器,这一点对于React来说相当方便,因为可以在组件中使用模块化CSS。而一般的项目中可以不用到这个加载器。
    • module 的作用是添加loaders, 那loaders有什么作用呢?

模块加载(转换)器 Loader

  • loader:模块加载器,将非js模块包装成webpack能理解的js模块;
  • loader用于转换应用程序的资源文件。他们是运行在nodejs下的函数,使用参数来获取一个资源的来源,并且返回一个新的来源(资源的位置)。
  • 文件loader:
    • url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URL
    • file-loader 将文件发送到输出文件夹,并返回(相对)URL
  • JSON的loader
    • json-loader 加载 JSON 文件(默认包含)
  • ES5-6的loader
    • babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5
  • css的loader
    • style-loader 将模块的导出作为样式添加到 DOM 中
    • css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码
    • less-loader 加载和转译 LESS 文件
    • sass-loader 加载和转译 SASS/SCSS 文件
    • stylus-loader 加载和转译 Stylus 文件
  • 代码规范loader
    • eslint-loader PreLoader,使用 ESLint 清理代码
  • vue的loader

    • vue-loader 加载和转译 Vue 组件
  • resolve

    • alias可以用于定义别名,用过seajs等模块工具的都知道alias的作用,比如我们在这里定义了ui这个别名,那么在模块中想引用ui目录下的文件,就可以直接这样写:require('ui/dialog.js');不用加上前面的更长的文件路径。
resolve: {
    //查找module的话从这里开始查找
    root: 'E:/github/flux-example/src', //绝对路径
    //自动扩展文件后缀名,意味着我们require模块可以省略不写后缀名
    extensions: ['', '.js', '.json', '.scss'],
    //模块别名定义,方便后续直接引用别名,无须多写长长的地址
    alias: {
        AppStore : 'js/stores/AppStores.js',//后续直接 require('AppStore') 即可
        ActionType : 'js/actions/ActionType.js',
        AppAction : 'js/actions/AppAction.js'
    }
}

loader学习

  • 学习网址:https://webpack.js.org/concepts/loaders/
  • Webpack 本身只能处理原生的js模块,但是loader转换器可以将各种类型的资源转换成js模块。这样,任何资源都可以成为Webpack可以处理的模块。

使用loader的三种方式

  • 在文件中直接引入loader文件,es6的语法
//es6的语法
import Styles from 'style-loader!css-loader?modules!./styles.css';
//commonjs语法
require('style-loader!css-loader?modules!./styles.css')
  • 在命令行界面使用的方式CLI
//直接执行
webpack --module-bind jade-loader --module-bind 'css=style-loader!css-loader'
  • 在配置文件中使用loader
module: {
    rules: [
      {
        <!-- 首先对资源一个正则匹配 -->
        test: /\.css$/,
        <!-- 匹配成功后会使用多个loader对其进行处理 -->
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }

babel

  • Babel通过语法转换器支持最新版本的JavaScript。这些插件允许你立刻使用新语法,无需等待浏览器支持。
  • 在src文件夹下创建component文件夹,component里面创建layer组件,组件里有js、css、html文件;
  • 在src文件夹中创建入口js文件,app.js;
//app.js中引入html
import layer from './component/layer/layer.js'
const App = function() {
    console.log(layer)
}
new App()

//layer.js中渲染模板
import tpl from './layer.html'
function layer() {
    return {
        name: 'layer',
        tpl: tpl
    }
}
export default layer;
  • 安装babel转化es6的代码npm install --save-dev babel-loader babel-core(webpack3版本)
  • 使用babel加载器将es6的语法进行转化,转化时要指定参数,方式有三种:
    • 使用配置指定
//在配置webpack.config.js中添加
module: {
        rules: [{
            test: /\.js$/,
            loader: "babel-loader",
            //加快打包速度的配置
            //排除范围
            exclude: __dirname + '/node_modules/',
            //babel-loader的处理范围
            include: '/src/',
            //webpack3中使用options代替query
            options: {
                'presets': ['env']
            }
        }]
    }
  • 在package.json中添加
"babel":{
  "presets":["env"];
}
  • 根目录下创建.babelrc文件
{
  "presets": ["env"]
} 

插件 Plugin

  • plugin:插件,在webpack构建流程中的特定时机插入具有特定功能的代码;
    Alt text
//使用了CommonsChunkPlugin用于生成公用代码,不只可以生成一个,还能根据不同页面的文件关系,自由生成多个,例如:
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");
module.exports = {
    entry: {
        p1: "./page1",
        p2: "./page2",
        p3: "./page3",
        ap1: "./admin/page1",
        ap2: "./admin/page2"
    },
    output: {
        filename: "[name].js"
    },
    plugins: [
        new CommonsChunkPlugin("admin-commons.js", ["ap1", "ap2"]),
        new CommonsChunkPlugin("commons.js", ["p1", "p2", "admin-commons.js"])
    ]
};
// 在不同页面用<script>标签引入如下js:
// page1.html: commons.js, p1.js
// page2.html: commons.js, p2.js
// page3.html: p3.js
// admin-page1.html: commons.js, admin-commons.js, ap1.js
// admin-page2.html: commons.js, admin-commons.js, ap2.js
  • 例:
module.exports = {
    devtool: "source-map",    //生成sourcemap,便于开发调试
    entry: getEntry(),         //获取项目入口js文件
    output: {
        path: path.join(__dirname, "dist/js/"), //文件输出目录
        publicPath: "dist/js/",        //用于配置文件发布路径,如CDN或本地服务器
        filename: "[name].js",        //根据入口文件输出的对应多个文件名
    },
    module: {
        //各种加载器,即让各种文件格式可用require引用
        loaders: [
            // { test: /\.css$/, loader: "style-loader!css-loader"},
            // { test: /\.less$/, loader: "style-loader!csss-loader!less-loader"}
        ]
    },
    resolve: {
        //配置别名,在项目中可缩减引用路径
        alias: {
            jquery: srcDir + "/js/lib/jquery.min.js",
            core: srcDir + "/js/core",
            ui: srcDir + "/js/ui"
        }
    },
    plugins: [
        //提供全局的变量,在模块中使用无需用require引入
        new webpack.ProvidePlugin({
            jQuery: "jquery",
            $: "jquery",
            // nie: "nie"
        }),
        //将公共代码抽离出来合并为一个文件
        new CommonsChunkPlugin('common.js'),
        //js文件的压缩
        new uglifyJsPlugin({
            compress: {
                warnings: false
            }
        })
    ]
};

webpack-dev-server

  • webpack-dev-server是一个轻量级的服务器,修改文件源码后,自动刷新页面将修改同步到页面上;
  • webpack-dev-server的他爹和他爹的朋友
    • webpack-dev-middleware:作为一个 webpack 中间件,它会开启 watch mode 监听文件变更,并自动地在内存中快速地重新打包、提供新的 bundle,自动编译(watch mode)+速度快(全部走内存)。
    • webpack-hot-middleware:
      • webpack 可以通过配置 webpack.HotModuleReplacementPlugin 插件来开启全局的 HMR 能力;
      • 开启后 bundle 文件会变大一些,因为它加入了一个小型的 HMR 运行时(runtime),当你的应用在运行的时候,webpack 监听到文件变更并重新打包模块时,HMR 会判断这些模块是否接受 update,若允许,则发信号通知应用进行热替换。
  • webpack-dev-server是一个小型的Node.js Express服务器,它使用webpack-dev-middleware来服务于webpack的包,除此自外,它还有一个通过Sock.js来连接到服务器的微型运行时
  • webpack-dev-server是一个独立的NPM包,你可以通过npm install webpack-dev-server来安装它。
  • webpack-dev-server配置
var WebpackDevServer = require("webpack-dev-server");
var webpack = require("webpack");

var compiler = webpack({});
var server = new WebpackDevServer(compiler, {

  contentBase: "/path/to/directory",

  hot: true,

  historyApiFallback: false,

  compress: true,

  proxy: {
    "**": "http://localhost:9090"
  },
  setup: function(app) {},
  staticOptions: {},
  quiet: false,
  noInfo: false,
  lazy: true,
  filename: "bundle.js",
  watchOptions: {
    aggregateTimeout: 300,
    poll: 1000
  },
  publicPath: "/assets/",
  headers: { "X-Custom-Header": "yes" },
  stats: { colors: true }
});
server.listen(8080, "localhost", function() {});

devServer配置项

  • contentBase
    • 即 SERVERROOT,如 “path.join(__dirname, “src/html”)”,后续访问 http://localhost:3333/index.html 时,SERVER 会从 src/html 下去查找 index.html 文件。
    • 它可以是单个或多个地址的形式:(若不填写该项,默认为项目根目录。)
//单个
contentBase: path.join(__dirname, "public")
//多个:
contentBase: [path.join(__dirname, "public"), path.join(__dirname, "assets")]
  • port
    • 即监听端口,默认为8080。
  • compress
    • 传入一个 boolean 值,通知 SERVER 是否启用 gzip。
  • hot
    • 传入一个 boolean 值,通知 SERVER 是否启用 HMR。
  • https
    • 可以传入 true 来支持 https 访问,也支持传入自定义的证书:
https: true
//也可以传入一个对象,来支持自定义证书
https: {
  key: fs.readFileSync("/path/to/server.key"),
  cert: fs.readFileSync("/path/to/server.crt"),
  ca: fs.readFileSync("/path/to/ca.pem"),
}
  • proxy
    • 代理配置,适用场景是,除了 webpack-dev-server 的 SERVER(SERVER A) 之外,还有另一个在运行的 SERVER(SERVER B),而我们希望能通过 SERVER A 的相对路径来访问到 SERVER B 上的东西。
devServer: {
    contentBase: path.join(__dirname, "src/html"),
    port: 3333,
    hot: true,
    proxy: {
        "/api": "http://localhost:5050"
    }
}
//运行 webpack-dev-server 后,若访问 http://localhost:3333/api/user,则相当于访问 http://localhost:5050/api/user。
  • publicPath

    • 如同 webpack-dev-middleware 的 publicPath 一样,表示从内存中的哪个路径去存放和检索静态文件;
    • 不过官方文档有一处错误需要堪正 —— 当没有配置 devServer.publicPath 时,默认的 devServer.publicPath 并非根目录,而是 output.publicPath;
    • 这也是为何咱们的例子里压根没写 devServer.publicPath,但还能正常请求到 https://localhost:3333/assets/bundle.js
  • setup

    • webpack-dev-server 的服务应用层使用了 express,故可以通过 express app 的能力来模拟数据回包,devServer.setup 方法就是干这事的:
devServer: {
   contentBase: path.join(__dirname, "src/html"),
   port: 3333,
   hot: true,
   setup(app){  //模拟数据
       app.get('/getJSON', function(req, res) {
           res.json({ name: 'vajoy' });
       });
   }
}

webpack插件

  • webpack 通过 plugins 实现各种功能。常见的 plugins 如下:

    • webpack.DefinePlugin 定义环境变量;
    const webpack = require('webpack'); 
    const NODE_ENV = process.env.NODE_ENV; // 从命令行环境获取 NODE_ENV 参数 
    module.exports = { 
        plugins: [ 
            new webpack.DefinePlugin({ 
                'process.env': { 
                    'NODE_ENV': JSON.stringify(NODE_ENV) 
                    } // 定义浏览器中的替换的变量为 `process.env.NODE_ENV` 
            })
         ] 
     }
    • webpack.EnvironmentPlugin 定义环境变量;
    const webpack = require('webpack');
    module.exports = {
        plugins: [
            new webpack.EnvironmentPlugin([
                'NODE_ENV'
            ])
        ]
    }
    • webpack.optimize.CommonsChunkPlugin 共用 js 打包
    • html-webpack-plugin 使用模版生成 html 文件
    • webpack-visualizer-plugin 输出依赖文件分析图表
    • webpack.HotModuleReplacementPlugin 代码热更新,用于调试模式
    • webpack.optimize.OccurrenceOrderPlugin 调整模块的打包顺序,用到次数更多的会出现在文件的前面
    • webpack.NoErrorsPlugin 构建过程中有报错,不认为构建完成
    • webpack.ProgressPlugin 输出构建进度
    • webpack.BannerPlugin 在文件头添加注释
    • webpack.optimize.UglifyJsPlugin 压缩 js
    • webpack.optimize.DedupePlugin 去除重复依赖
    • extract-text-webpack-plugin 从 js 中提取出样式文件,单独打包成 css 文件;
    • clean-webpack-plugin每次运行清除指定目录下打包文件;

extract-text-webpack-plugin

  • 作用:该插件的主要是为了抽离css样式,防止将样式打包在js中引起页面样式加载错乱的现象;
  • 安装:npm install extract-text-webpack-plugin --save-dev
  • 插件参数:
    • use:指需要什么样的loader去编译文件,这里由于源文件是.css所以选择css-loader
    • fallback:编译后用什么loader来提取css文件
    • publicfile:用来覆盖项目路径,生成该css文件的文件路径
  • 使用:
const ExtractTextPlugin = require("extract-text-webpack-plugin");

module.exports = {
  module: {
    rules: [
      {
        test: /\.css$/,
        use: ExtractTextPlugin.extract({
          fallback: "style-loader", // 编译后用什么loader来提取css文件
          use: "css-loader" // 指需要什么样的loader去编译文件,这里由于源文件是.css所以选择css-loader
        })
      }
    ]
  },
  plugins: [
    new ExtractTextPlugin("styles.css"),
  ]
}

开发/生产环境打包

  • webpack.base.conf.js
  • 开启source Map ,帮助调试

webpack项目中的最佳配置

  • webpack官方提供的配置方法是通过module.exports返回一个json,但是这种场景不灵活,不能适配多种场景。
  • 比如要解决:production模式和development模式,webpack的配置是有差异的,大致有两种思路。
    • 1、两份配置文件webpack.config.production.js/webpack.config.development.js,然后不同场景下,使用不同的配置文件。
    • 2、通过module.exports返回函数,该函数能接受参数。
    • 相对来说,第一种更简单,但是重复配置多;第二种更灵活,推荐第二种方式。
//返回函数的方式的配置代码架子如下:
module.exports = function(env) {
    return {
        //上下文
        context: config.context,
        //入口文件,是所有依赖关系的入口,webpack从这个入口开始静态解析,分析模块之间的依赖关系。
        entry: config.src,
        //打包输出的配置
        output: {
            path: path.join(config.jsDest, project),
            filename: '[name].js',
            chunkFilename: '[name].[chunkhash:8].js',
            publicPath: '/assets/' + project + '/'
        },
        //SourceMap选项,便于开发模式下调试。
        devtool: "eval",
        //监听模式,增量更新,开发必备!
        watch: false,
        //优化。
        profile: true,
        //webpack构建的过程中会生成很多临时的文件,打开cache可以让这些临时的文件缓存起来,从而更快的构建。
        cache: true,
        //loaders用来对文件做预处理。这样webpack就可以打包任何静态文件。
        module: {
            loaders: getLoaders(env)
        },
        //模块别名,这样可以更方便的引用模块。
        resolve: {
            alias: getAlias(env)
        },
        //webpack的一些内置功能均是以插件的形式提供。
        plugins: getPlugins(env)
    };
}

gitHub参考地址:https://github.com/mutouafangzi/webpackDemo20180222

猜你喜欢

转载自blog.csdn.net/mutouafangzi/article/details/79529002