webpack中文网(五)shim(polyfill、旧环境、全局变量)、渐进式网络应用程序(PWA、Workbox、Service Worker服务器通信)、TypeScript(类型、ts-loader)
总结:
shimming
总结
- shim 是一个库(library),它将一个新的 API 引入到一个旧的环境中,而且仅靠旧的环境中已有的手段实现。polyfill 就是一个用在浏览器 API 上的 shim。我们通常的做法是先检查当前浏览器是否支持某个 API,如果不支持的话就加载对应的 polyfill。然后新旧浏览器就都可以使用这个 API 了。
适用于全局依赖、全局变量,这些“不符合规范的模块”。
不推荐使用全局的东西!请只在必要的时候才使用本文所述的这些特性。
shimming全局变量
- 将lodash模块当做全局变量
- 使用
ProvidePlugin
插件告诉 webpack:
- 如果你遇到了至少一处用到
lodash
变量的模块实例,那请你将lodash
package 包引入进来,并将其提供给需要用到它的模块。- 结合tree shaking ,将
lodash
库中的其他没用到的部分去除。
- 还可以使用
ProvidePlugin
暴露某个模块中单个导出值,只需通过一个“数组路径”进行配置(例如[module, child, ...children?]
)全局exports
- 在Node.js中,可以使用require.resolve函数来查询某个模块文件的带有完整绝对路径的文件名
polyfills
polyfill本意是聚酯纤维填充,这里用作补丁或者兼容插件讲,用来兼容原本一些不支持的属性和方法
比如:有些旧浏览器不支持
Number.isNaN
方法,Polyfill就可以是这样的:if(!Number.isNaN) { Number.isNaN = function(num) { return(num !== num); } }
假如浏览器没有
Number.isNaN
方法,那就写一个polyfill给它添加上去,Polyfill就是类似这样解决兼容问题的同理:polyfill也可以解决es6的兼容问题 比如babel-polyfill
polyfills 虽然是一种模块引入方式,但是并不推荐在主 bundle 中引入 polyfills,因为这不利于具备这些模块功能的现代浏览器用户,会使他们下载体积很大、但却不需要的脚本文件。
whatwg-fetch
polyfill:「将 polyfills 提供给那些需要引入它的用户」渐进式网络应用程序(PWA)
- 简介
- 渐进式网络应用程序(Progressive Web Application - PWA),是一种可以提供类似于原生应用程序(native app)体验的网络应用程序(web app)。
- WA 可以用来做很多事。其中最重要的是,在**离线(offline)**时应用程序能够继续运行功能。这是通过使用名为 Service Workers 的网络技术来实现的。
- 构建服务器通讯
- 使用 http-server package 包:
npm install http-server --save-dev
- 还要修改
package.json
的scripts
部分,来添加一个start
脚本- 运行命令
npm run build
来构建你的项目- 如果你打开浏览器访问
http://localhost:8080
(即http://127.0.0.1
),你应该会看到在dist
目录创建出服务,并可以访问 webpack 应用程序。如果停止服务器然后刷新,则 webpack 应用程序不再可访问。- 使用 Workbox 项目构建了一个离线应用程序。
- 添加 workbox-webpack-plugin 插件,并调整
webpack.config.js
文件- 有了 Workbox,再执行
npm run build
- 生成了 2 个额外的文件:
sw.js
和体积很大的precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js
。sw.js
是 Service Worker 文件,precache-manifest.b5ca1c555e832d6fbf9462efd29d27eb.js
是sw.js
引用的文件,所以它也可以运行。可能在你本地生成的文件会有所不同;但是你那里应该会有一个sw.js
文件。- 注册 Service Worker,使其出场并开始表演
- 再次运行
npm build build
来构建包含注册代码版本的应用程序。然后用npm start
启动服务。访问http://localhost:8080
并查看 console 控制台。- 现在来进行测试。停止服务器并刷新页面。如果浏览器能够支持 Service Worker,你应该可以看到你的应用程序还在正常运行。然而,服务器已经停止了服务,此刻是 Service Worker 在提供服务。
TypeScript
TypeScript 是 JavaScript 的超集,为其增加了类型系统,可以编译为普通的 JavaScript 代码。
typescript相对于ES5有五大改善:
- 类型
- 杜绝手误导致的变量名写错。
- 自动完成。
- 重构支持。
- 类型可以一定程度上充当文档
- 类
- 注解
- 模块导入
- 语言工具包(比如,结构)
typescript相对于ES6,TypeScript最大的改善是增加了类型系统,在我看来 TypeScript 相对于 JavaScript ,除了静态类型外没带来任何东西。
TypeScript 增加了代码的可读性和可维护性
- 类型系统实际上是最好的文档,大部分的函数看看类型的定义就可以知道如何使用了
- 可以在编译阶段就发现大部分错误,这总比在运行时候出错好
- 增强了编辑器和 IDE 的功能,包括代码补全、接口提示、跳转到定义、重构等
TypeScript 非常包容
- TypeScript 是 JavaScript 的超集,
.js
文件可以直接重命名为.ts
即可- 即使不显式的定义类型,也能够自动做出类型推论
- 可以定义从简单到复杂的几乎一切类型
- 即使 TypeScript 编译报错,也可以生成 JavaScript 文件
- 兼容第三方库,即使第三方库不是用 TypeScript 写的,也可以编写单独的类型文件供 TypeScript 读取
TypeScript 的缺点
任何事物都是有两面性的,我认为 TypeScript 的弊端在于:
- 有一定的学习成本,需要理解接口(Interfaces)、泛型(Generics)、类(Classes)、枚举类型(Enums)等前端工程师可能不是很熟悉的概念
- 短期可能会增加一些开发成本,毕竟要多写一些类型的定义,不过对于一个需要长期维护的项目,TypeScript 能够减少其维护成本
- 集成到构建流程需要一些工作量
- 可能和一些库结合的不是很完美
1. shimming(细粒度)
webpack
编译器(compiler)能够识别遵循 ES2015 模块语法、CommonJS 或 AMD 规范编写的模块。然而,一些第三方的库(library)可能会引用一些全局依赖(例如 jQuery
中的 $
)。这些库也可能创建一些需要被导出的全局变量。这些“不符合规范的模块”就是 shimming 发挥作用的地方。
**我们不推荐使用全局的东西!**在 webpack 背后的整个概念是让前端开发更加模块化。也就是说,需要编写具有良好的封闭性(well contained)、彼此隔离的模块,以及不要依赖于那些隐含的依赖模块(例如,全局变量)。请只在必要的时候才使用本文所述的这些特性。
shimming 另外一个使用场景就是,当你希望 polyfill 浏览器功能以支持更多用户时。在这种情况下,你可能只想要将这些 polyfills 提供给到需要修补(patch)的浏览器(也就是实现按需加载)。
下面的文章将向我们展示这两种用例。
为了方便,本指南继续沿用起步中的代码示例。在继续之前,请确保你已经熟悉那些配置。
shimming 全局变量
让我们开始第一个 shimming 全局变量的用例。在此之前,我们先看看我们的项目:
project
webpack-demo
|- package.json
|- webpack.config.js
|- /dist
|- /src
|- index.js
|- /node_modules
还记得我们之前用过的 lodash
吗?出于演示的目的,让我们把这个模块作为我们应用程序中的一个全局变量。要实现这些,我们需要使用 ProvidePlugin
插件。
使用 ProvidePlugin
后,能够在通过 webpack 编译的每个模块中,通过访问一个变量来获取到 package 包。如果 webpack 知道这个变量在某个模块中被使用了,那么 webpack 将在最终 bundle 中引入我们给定的 package。让我们先移除 lodash
的 import
语句,并通过插件提供它:
src/index.js
- import _ from 'lodash';
-
function component() {
var element = document.createElement('div');
- // Lodash, now imported by this script
element.innerHTML = _.join(['Hello', 'webpack'], ' ');
return element;
}
document.body.appendChild(component());
webpack.config.js
const path = require('path');
+ const webpack = require('webpack');
module.exports = {
entry: './src/index.js',
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'dist')
- }
+ },
+ plugins: [
+ new webpack.ProvidePlugin({
+ _: 'lodash'
+ })
+ ]
};
本质上,我们所做的,就是告诉 webpack……
如果你遇到了至少一处用到
lodash
变量的模块实例,那请你将lodash
package 包引入进来,并将其提供给需要用到它的模块。
如果我们 run build,将会看到同样的输出:
Hash: f450fa59fa951c68c416
Version: webpack 2.2.0
Time: 343ms
Asset Size Chunks Chunk Names
bundle.js 544 kB 0 [emitted] [big] main
[0] ./~/lodash/lodash.js 540 kB {
0} [built]
[1] (webpack)/buildin/global.js 509 bytes {
0} [built]
[2] (webpack)/buildin/module.js 517 bytes {
0} [built]
[3] ./src/index.js 189 bytes {
0} [built]
我们还可以使用 ProvidePlugin
暴露某个模块中单个导出值,只需通过一个“数组路径”进行配置(例如 [module, child, ...children?]
)。所以,让我们做如下设想,无论 join
方法在何处调用,我们都只会得到的是 lodash
中提供的 join
方法。
src/index.js
function component() {
var element = document.createElement('div');
- element.innerHTML = _.join(['Hello', 'webpack'], ' ');
+ element.innerHTML = join(['Hello', 'webpack'], ' ');
return element;
}
document