资讯专栏INFORMATION COLUMN

傻傻分不清的Manifest

printempw / 1002人阅读

摘要:的英文含义是名单种技术的确都是把当做清单使用缓存清单清单打包资源路径清单打包清单只不过是在不同的场景中使用特定的清单来完成某些功能所以,学好英文是多么重要,这样才不会傻傻分不清到底是干啥的

在前端,说到manifest,其实是有歧义的,就我了解的情况来说,manifest可以指代下列含义:

html标签的manifest属性: 离线缓存(目前已被废弃)

PWA: 将Web应用程序安装到设备的主屏幕

webpack中webpack-manifest-plugin插件打包出来的manifest.json文件,用来生成一份资源清单,为后端渲染服务

webpack中DLL打包时,输出的manifest.json文件,用来分析已经打包过的文件,优化打包速度和大小

下面我们来一一介绍下

html属性




  
  
  
  Document
  
  
  


 


浏览器解析这段html标签时,就会去访问tc.mymanifest这个文件,这是一个缓存清单文件

tc.mymanifest

# v1 这是注释
CACHE MANIFEST
/theme.css
/main.js

NETWORK:
*

FALLBACK:
/html5/ /404.html

CACHE MANIFEST指定需要缓存的文件,第一次下载完成以后,文件都不会再从网络请求了,即使用户不是离线状态,除非tc.mymanifest更新了,缓存清单更新之后,才会再次下载。标记了manifest的html本身也被缓存

NETWORK指定非缓存文件,所有类似资源的请求都会绕过缓存,即使用户处于离线状态,也不会读缓存

FALLBACK指定了一个后备页面,当资源无法访问时,浏览器会使用该页面。
比如离线访问/html5/目录时,就会用本地的/404.html页面

缓存清单可以是任意后缀名,不过必须指定content-type属性为text/cache-manifest

那如何更新缓存?一般有以下几种方式:

用户清空浏览器缓存

manifest 文件被修改(即使注释被修改)

由程序来更新应用缓存

需要特别注意:用户第一次访问该网页,缓存文件之后,第二次进入该页面,发现tc.mymanifest缓存清单更新了,于是会重新下载缓存文件,但是,第二次进入显示的页面仍然执行的是旧文件,下载的新文件,只会在第三次进入该页面后执行!!!

如果希望用户立即看到新内容,需要js监听更新事件,重新加载页面

window.addEventListener("load", function (e) {

  window.applicationCache.addEventListener("updateready", function (e) {

    if (window.applicationCache.status == window.applicationCache.UPDATEREADY) {
      // 更新缓存
      // 重新加载
      window.applicationCache.swapCache();
      window.location.reload();

    } else {

    }

  }, false);

}, false);

建议对tc.mymanifest缓存清单设置永不缓存

不过,manifest也有很多缺点,比如需要手动一个个填写缓存的文件,更新文件之后需要二次刷新,如果更新的资源中有一个资源更新失败了,将导致全部更新失败,将用回上一版本的缓存

HTML5规范也废弃了这个属性,因此不建议使用

PWA

为了实现PWA应用添加至桌面的功能,除了要求站点支持HTTPS之外,还需要准备 manifest.json文件去配置应用的图标、名称等信息

{ 
"name" : "Minimal PWA" , 
"short_name" : "PWA Demo" , 
"display" : "standalone" , 
"start_url" : "/" , 
"theme_color" : "#313131" , 
"background_color" : "#313131" , 
"icons" : [ 
  {
    "src": "images/touch/homescreen48.png",
    "sizes": "48x48",
    "type": "image/png"
  }
 ] 
}

通过一系列配置,就可以把一个PWA像APP一样,添加一个图标到手机屏幕上,点击图标即可打开站点

基于webpack的react开发环境

本文默认你已经了解最基本的webpack配置,如果完全不会,建议看下这篇文章

我们首先搭建一个最简单的基于webpack的react开发环境

源代码地址:https://github.com/deepred5/l...

mkdir learn-dll
cd learn-dll

安装依赖

npm init -y
npm install @babel/polyfill react react-dom --save
npm install webpack webpack-cli webpack-dev-server @babel/core @babel/preset-env @babel/preset-react add-asset-html-webpack-plugin autoprefixer babel-loader clean-webpack-plugin css-loader html-webpack-plugin mini-css-extract-plugin node-sass postcss-loader sass-loader style-loader --save-dev

新建.bablerc

{
  "presets": [
    [
      "@babel/preset-env",
      {
        "useBuiltIns": "usage", // 根据browserslis填写的浏览器,自动添加polyfill
        "corejs": 2,
      }
    ],
    "@babel/preset-react" // 编译react
  ],
  "plugins": []
}

新建postcss.config.js

module.exports = {
  plugins: [
    require("autoprefixer") // 根据browserslis填写的浏览器,自动添加css前缀
  ]
}

新建.browserslistrc

last 10 versions
ie >= 11
ios >= 9
android >= 6

新建webpack.dev.js(基本配置不再详细介绍)

const path = require("path");
const HtmlWebpackPlugin = require("html-webpack-plugin");

module.exports = {
  mode: "development",
  devtool: "cheap-module-eval-source-map",
  entry: {
    main: "./src/index.js"
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].js",
    chunkFilename: "[name].chunk.js",
  },
  devServer: {
    historyApiFallback: true,
    overlay: true,
    port: 9001,
    open: true,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /.css$/,
        use: ["style-loader",
          "css-loader",
          "postcss-loader"
        ],
      },
      {
        test: /.scss$/,
        use: ["style-loader",
          {
            loader: "css-loader",
            options: {
              modules: false,
              importLoaders: 2
            }
          },
          "sass-loader",
          "postcss-loader"
        ],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }), // index打包模板
  ]
}

新建src目录,并新建src/index.html




  
  
  
  learn dll


  

新建src/Home.js

import React from "react";
import "./Home.scss";

export default () => 
home

新建src/Home.scss

.home {
  color: red;
}

新建src/index.js

import React, { Component } from "react";
import ReactDom from "react-dom";
import Home from "./Home";

class Demo extends Component {
  render() {
    return (
      
    )
  }
}

ReactDom.render(, document.getElementById("app"));

修改package.json

"scripts": {
  "dev": "webpack-dev-server --config webpack.dev.js"
},

最后,运行npm run dev,应该可以看见效果

新建webpack.prod.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");


module.exports = {
  mode: "production",
  entry: {
    main: "./src/index.js"
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].[contenthash:8].js",
    chunkFilename: "[name].[contenthash:8].chunk.js",
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /.css$/,
        use: [MiniCssExtractPlugin.loader, // 多带带提取css文件
          "css-loader",
          "postcss-loader"
        ],
      },
      {
        test: /.scss$/,
        use: [MiniCssExtractPlugin.loader,
        {
          loader: "css-loader",
          options: {
            modules: false,
            importLoaders: 2
          }
        },
          "sass-loader",
          "postcss-loader"
        ],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash:8].css",
      chunkFilename: "[id].[contenthash:8].css",
    }),
    new CleanWebpackPlugin(), // 打包前先删除之前的dist目录
  ]
};

修改package.json,添加一句"build": "webpack --config webpack.prod.js"

运行npm run build,可以看见打包出来的dist目录

html,js,css都多带带分离出来了

至此,一个基于webpack的react环境搭建完成

webpack-manifest-plugin

通常情况下,我们打包出来的js,css都是带上版本号的,通过HtmlWebpackPlugin可以自动帮我们在index.html里面加上带版本号的js和css




  
  
  
  learn dll


  

但是在某些情况,index.html模板由后端渲染,那么我们就需要一份打包清单,知道打包后的文件对应的真正路径

安装插件webpack-manifest-plugin

npm i webpack-manifest-plugin -D

修改webpack.prod.js

const ManifestPlugin = require("webpack-manifest-plugin");
module.exports = {
    // ...
    plugins: [
      new ManifestPlugin()
    ]
};

重新打包,可以看见dist目录新生成了一个manifest.json

{
  "main.css": "main.198b3634.css",
  "main.js": "main.d312f172.js",
  "index.html": "index.html"
}

比如在SSR开发时,前端打包后,node后端就可以通过这个json数据,返回正确资源路径的html模板

const buildPath = require("./dist/manifest.json");

res.send(`
  


  
  
  
  ssr


  
`);
代码分割

我们之前的打包方式,有一个缺点,就是把业务代码和库代码都统统打到了一个main.js里面。每次业务代码改动后,main.js的hash值就变了,导致客户端又要重新下载一遍main.js,但是里面的库代码其实是没改变的!

通常情况下,react react-dom之类的库,都是不经常改动的。我们希望多带带把这些库代码提取出来,生成一个vendor.js,这样每次改动代码,只是下载main.jsvendor.js可以充分缓存(也就是所谓的代码分割code splitting)

webpack4自带代码分割功能,只要配置:

optimization: {
  splitChunks: {
    chunks: "all"
  }
}

webpack.prod.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");

module.exports = {
  mode: "production",
  entry: {
    main: "./src/index.js"
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].[contenthash:8].js",
    chunkFilename: "[name].[contenthash:8].chunk.js",
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /.css$/,
        use: [MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader"
        ],
      },
      {
        test: /.scss$/,
        use: [MiniCssExtractPlugin.loader,
        {
          loader: "css-loader",
          options: {
            modules: false,
            importLoaders: 2
          }
        },
          "sass-loader",
          "postcss-loader"
        ],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash:8].css",
      chunkFilename: "[id].[contenthash:8].css",
    }),
    new CleanWebpackPlugin(),
    new ManifestPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: "all"
    }
  }
};

重新打包,发现新生成了一个vendor.js文件,公用的一些代码就被打包进去了

重新修改src/Home.js,然后打包,你会发现vendor.js的hash没有改变,这也是我们希望的

DLL打包

上面的打包方式,随着项目的复杂度上升后,打包速度会开始变慢。原因是,每次打包,webpack都要分析哪些是公用库,然后把他打包到vendor.js

我们可不可以在第一次构建vendor.js以后,下次打包,就直接跳过那些被打包到vendor.js里的代码呢?这样打包速度可以明显提升

这就需要DllPlugin结合DllRefrencePlugin插件的运用

dll打包原理就是:

把指定的库代码打包到一个dll.js,同时生成一份对应的manifest.json文件

webpack打包时,读取manifest.json,知道哪些代码可以直接忽略,从而提高构建速度

我们新建一个webpack.dll.js

const path = require("path");
const webpack = require("webpack");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");

module.exports = {
  mode: "production",
  entry: {
    vendors: ["react", "react-dom"] // 手动指定打包哪些库
  },
  output: {
    filename: "[name].[hash:8].dll.js",
    path: path.resolve(__dirname, "./dll"),
    library: "[name]"
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DllPlugin({
      path: path.join(__dirname, "./dll/[name].manifest.json"), // 生成对应的manifest.json,给webpack打包用
      name: "[name]",
    }),
  ],
}

添加一条命令:

"build:dll": "webpack --config webpack.dll.js"

运行dll打包

npm run build:dll

发现生成一个dll目录

修改webpack.prod.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
const webpack = require("webpack");

module.exports = {
  mode: "production",
  entry: {
    main: "./src/index.js"
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].[contenthash:8].js",
    chunkFilename: "[name].[contenthash:8].chunk.js",
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /.css$/,
        use: [MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader"
        ],
      },
      {
        test: /.scss$/,
        use: [MiniCssExtractPlugin.loader,
        {
          loader: "css-loader",
          options: {
            modules: false,
            importLoaders: 2
          }
        },
          "sass-loader",
          "postcss-loader"
        ],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash:8].css",
      chunkFilename: "[id].[contenthash:8].css",
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, "./dll/vendors.manifest.json") // 读取dll打包后的manifest.json,分析哪些代码跳过
    }),
    new CleanWebpackPlugin(),
    new ManifestPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: "all"
    }
  }
};

重新npm run build,发现dist目录里,vendor.js没有了

这是因为react,react-dom已经打包到dll.js里了,webpack读取manifest.json之后,知道可以忽略这些代码,于是就没有再打包了

但这里还有个问题,打包后的index.html还需要添加dll.js文件,这就需要add-asset-html-webpack-plugin插件

npm i add-asset-html-webpack-plugin -D

修改webpack.prod.js

const path = require("path");
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const HtmlWebpackPlugin = require("html-webpack-plugin");
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const ManifestPlugin = require("webpack-manifest-plugin");
const webpack = require("webpack");
const AddAssetHtmlPlugin = require("add-asset-html-webpack-plugin");

module.exports = {
  mode: "production",
  entry: {
    main: "./src/index.js"
  },
  output: {
    path: path.resolve(__dirname, "./dist"),
    filename: "[name].[contenthash:8].js",
    chunkFilename: "[name].[contenthash:8].chunk.js",
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        loader: "babel-loader"
      },
      {
        test: /.css$/,
        use: [MiniCssExtractPlugin.loader,
          "css-loader",
          "postcss-loader"
        ],
      },
      {
        test: /.scss$/,
        use: [MiniCssExtractPlugin.loader,
        {
          loader: "css-loader",
          options: {
            modules: false,
            importLoaders: 2
          }
        },
          "sass-loader",
          "postcss-loader"
        ],
      },
    ]
  },
  plugins: [
    new HtmlWebpackPlugin({ template: "./src/index.html" }),
    new AddAssetHtmlPlugin({ filepath: path.resolve(__dirname, "./dll/*.dll.js") }), // 把dll.js加进index.html里,并且拷贝文件到dist目录
    new MiniCssExtractPlugin({
      filename: "[name].[contenthash:8].css",
      chunkFilename: "[id].[contenthash:8].css",
    }),
    new webpack.DllReferencePlugin({
      manifest: path.resolve(__dirname, "./dll/vendors.manifest.json") // 读取dll打包后的manifest.json,分析哪些代码跳过
    }),
    new CleanWebpackPlugin(),
    new ManifestPlugin()
  ],
  optimization: {
    splitChunks: {
      chunks: "all"
    }
  }
};

重新npm run build,可以看见dll.js也被打包进dist目录了,同时index.html也正确引用




  
  
  
  learn dll


  

小结

我们介绍了4种manifest相关的前端技术。manifest的英文含义是名单, 4种技术的确都是把manifest当做清单使用:

缓存清单

PWA清单

打包资源路径清单

dll打包清单

只不过是在不同的场景中使用特定的清单来完成某些功能

所以,学好英文是多么重要,这样才不会傻傻分不清manifest到底是干啥的!

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/104443.html

相关文章

  • 傻傻分不清的__proto__与prototype

    摘要:今天同事小英童鞋问了我一个问题小英童鞋认为的原型对象是,所以会继承的属性,调用相当于调用,但结果不是一个方法。构造函数创建对象实例函数有两个不同的内部方法和。如果不通过关键字调用函数,则执行函数,从而直接执行代码中的函数体。 今天同事小英童鞋问了我一个问题: function Foo(firstName, lastName){ this.firstName = firstNam...

    YancyYe 评论0 收藏0
  • CSS中+ , > , ~ 傻傻不清

    摘要:选择器相邻兄弟选择器作用于紧跟其后的兄弟节点。注意事项有共同的父级必须紧跟其后,中间隔个啥都不行。注意事项要有共同的父级点击查看大气的,作用的范围广,限制少选择器后代选择器注意事项只作用于第一层后代点击查看只作用于第一层后代 +选择器(相邻兄弟选择器) 作用于紧跟其后的兄弟节点。 注意事项: 1.有共同的父级 2.必须紧跟其后,中间隔个啥都不行。 3.只作用于紧跟其后的元素...

    piglei 评论0 收藏0
  • CSS中+ , > , ~ 傻傻不清

    摘要:选择器相邻兄弟选择器作用于紧跟其后的兄弟节点。注意事项有共同的父级必须紧跟其后,中间隔个啥都不行。注意事项要有共同的父级点击查看大气的,作用的范围广,限制少选择器后代选择器注意事项只作用于第一层后代点击查看只作用于第一层后代 +选择器(相邻兄弟选择器) 作用于紧跟其后的兄弟节点。 注意事项: 1.有共同的父级 2.必须紧跟其后,中间隔个啥都不行。 3.只作用于紧跟其后的元素...

    joyqi 评论0 收藏0
  • 神经网络训练中,Epoch、Batch Size和迭代傻傻不清?

    摘要:损失代价的减小是一件好事只有在数据很庞大的时候在机器学习中,几乎任何时候都是,我们才需要使用,,迭代这些术语,在这种情况下,一次性将数据输入计算机是不可能的。 你肯定经历过这样的时刻,看着电脑屏幕抓着头,困惑着:「为什么我会在代码中使用这三个术语,它们有什么区别吗?」因为它们看起来实在太相似了。为了理解这些术语有什么不同,你需要了解一些关于机器学习的术语,比如梯度下降,以帮助你理解。这里简单...

    aisuhua 评论0 收藏0

发表评论

0条评论

printempw

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<