之前公司的官网项目静态文件都是放在公司自己的静态服务器中, 这其中的弊端就不赘述了. 简单说一下 CDN 的好处:
CDN 可以解决因分布, 带宽, 服务器性能带来的访问延迟问题, 适用于站点加速, 点播, 直播等场景. 使用户可就近取得所需内容, 解决 Internet 网络拥挤的状况, 提高用户访问网站的响应速度和成功率. 控制时延无疑是现代信息科技的重要指标, CDN 的意图就是尽可能的减少资源在转发, 传输, 链路抖动等情况下顺利保障信息的连贯性. CDN 就是扮演者护航者和加速者的角色, 更快准狠的触发信息和触达每一个用户, 带来更为极致的使用体验.
上传脚本文件
这里为了最小化演示路径, 我们只上传项目所用的 JS 文件. 且 CDN 基于七牛云.
- // webpack.config.JS
- module.exports = {
- // 入口文件
- entry: './src/app.js',
- // 打包出口
- output: {
- path: path.resolve(__dirname, 'dist'),
- filename: 'app.js',
- publicPath: 'http://example.clouddn.com/example/' // CDN 地址
- }
- }
在 output.publicPath 中可以配置我们的 CDN 地址 (这里你要有一个七牛云的 账户 https://www.qiniu.com/ ). 之后我们引入 https://www.npmjs.com/package/qn-webpack .
plugin 配置见该 plugin 源码即可. 这里有一份示例配置:
- // webpack.config.JS
- const qiniuWebpackPlugin = new QiniuWebpackPlugin({
- accessKey: '七牛云个人面板 > 密钥管理 > AK',
- secretKey: '七牛云个人面板 > 密钥管理 > SK',
- bucket: '对象存储空间名',
- path: '空间内保存路径',
- exclude: /index\.html$/ // 需要排除上传的文件
- });
- module.exports = {
- // 加载插件
- plugins: [ qiniuWebpackPlugin ]
- };
现在运行:
NPM run build # webpack --mode=production
在终端中我们已经可以看到我们的 JS 文件被上传至自己的 CDN 服务中.通过 HTML-webpack-plugin 我们引入 JS 的路径也相应的替换成了 CDN 地址.
上传图片
很多时候, 在我们的项目中, 需要去:
- import url from './your/img/path';
- <img src={
- url
- }>
图片挂上 CDN 也是很有必要的, 图片视频文件之类的东西本身就比较大而且可以算是静态的内容, 从动态服务器上分离出去, 可以极大的减小服务的负载. 特别像是七牛云这样拥有图片处理引擎的服务商, 我们还可以通过 imageView2 来处理上传至 CDN 的图片.
说回图片上传, 首先我们引入 ,Webpack 配置如下:
- // webpack.config.JS
- module.exports = {
- module: {
- rules: [
- {
- test: /\.(gif|PNG|jpe?g|svg|webp)$/i,
- use: [
- {
- loader: 'file-loader',
- options: {
- // publicPath: 默认 `__webpack_public_path__` 为 `output.publicPath`
- }
- }
- ]
- }
- ]
- }
- }
会指示 Webpack 将所需的对象作为文件引入并返回其公共 URL .
再次运行:
NPM run build # webpack --mode=production
两个文件已经被上传至 CDN , 且路径被替换. 但是, 2.74 MiB 与后面的 [big] 很是鲜艳, 下一步我们需要压缩图片以减少文件体积.
处理图片
之前我们可以看看 Chrome 控制台:
2.7 MiB 的图片就算放在 CDN , 请求时间也超过了 100ms, 那么我们开始处理图片.
引入 https://www.npmjs.com/package/image-webpack-loader 并开始配置 Webpack :
- //webpack.config.JS
- module.exports = {
- module: {
- rules: [{
- test: /\.(gif|PNG|jpe?g|svg|webp)$/i,
- use: [
- {
- loader: 'file-loader',
- options: {
- // publicPath: 默认 `__webpack_public_path__` 为 `output.publicPath`
- }
- }, {
- loader: 'image-webpack-loader',
- options: {
- mozjpeg: {
- progressive: true,
- quality: 65
- },
- optipng: {
- enabled: false,
- },
- pngquant: {
- quality: '65-90',
- speed: 4
- },
- gifsicle: {
- interlaced: false,
- },
- WebP: {
- quality: 75
- }
- }
- },
- ],
- }]
- }
- }
详细配置参见 文档 https://www.npmjs.com/package/image-webpack-loader .
再次打包:
发现图片大小从 2.74 MiB -> 1.16 MiB 虽然还是有点大, 但是已经有很明显的大小变化. 再来看看 Chrome 控制台:
效果很明显 140 ms -> 43 ms .
WebP
在进行这一步之前, 需要简单了解一下什么是 WebP 格式图片.
目前移动端 Android 4.0 以上, PC 端 Chorme 10+(14 ~ 16 有渲染 bug),Opera 11+ ,Safri 均支持 WebP 格式图片. WebP 与 jpg 相比较, 编码速度慢 10 倍, 解码速度慢 1.5 倍, 而绝大部分的网络应用中, 图片都是静态文件, 所以对于用户使用只需要关心解码速度即可. 但实际上, webp 虽然会增加额外的解码时间, 但是由于减少了文件体积, 缩短了加载的时间, 实际上文件的渲染速度反而变快了.
搬运知乎上的一张图片:
所以我们可以得出结论: WebP 体积大幅减少, 图片质量也有保障, 除了兼容性不太好.
对于兼容性, 我们可以看这张图:
考虑到兼容性的问题, 我们之后会进行专门处理. 现在第一步则是转化 PNG & jpg -> Webp .
这里选用 https://gulpjs.com/ 作为转化图片为 WebP 的自动化构建工具. Webpack 社区插件看 这里 https://github.com/ijs/webp-webpack-plugin , 我这里选用 gulp 作为构建工具也是希望自己能够控制 WebP 图片的制作, 在本地生成而不是 CI 打包时 (时间有点长... .
- //gulpfile.JS
- const gulp = require('gulp');
- const webp = require('gulp-webp'); // 基于 cwebp 的 gulp 插件
- // 基于 cwebp 转化图片
- gulp.task('webp', () =>
- gulp.src('src/img/*.{png,jpg,jpeg}')
- .pipe(webp({ quality: 75 })) // 详情配置见: https://github.com/imagemin/imagemin-webp#api
- .pipe(gulp.dest('src/img'))
- );
- // 监听文件夹变化
- gulp.task('watch', () =>
- gulp.watch('src/img/*.{png,jpg,jpeg}', ['webp'])
- );
- gulp.task('default', () =>
- gulp.start('watch')
- );
配置之后我们运行打包可以比较一下:
使用 WebP 格式前
使用 WebP 格式后
最后我们在项目中使用 WebP 时候, 需要判断一下浏览器是否支持 WebP 格式图片:
- const canUseWebp = (() => {
- return document.createElement('canvas').toDataURL('image/webp').indexOf('data:image/webp')> -1;
- })()
- // 如果可以使用 WebP , 则给顶部元素加上一个 class
- if (canUseWebp) {
- document.getElementsByTagName('body')[0].className += 'webp';
- }
将这段 JS 内联在 head 标签中后, 我们可以利用 CSS 预处理器来判断是否需要使用 WebP 格式图片.
- // stylus
- bg($url, $type)
- background-image url($url + $type)
- .webp & // 如果拥有 .webp 类名, 则使用 WebP 格式图片
- background-image url($url + '.webp')
- // Less
- .mixin(@url, @type) {
- background-image: url(@{url}.@{type});
- .webps & {
- background-image: url('@{url}.webp');
- }
- }
- // SCSS
- @mixin bg($url, $type) {
- background-image: url($url + $type);
- @at-root(with: all) .webps & {
- background-image: url($url + '.webp');
- }
- }
来源: https://juejin.im/post/5c08f4e9f265da61407eb486