0.前言
我們都知道pwa是一個新技術.,依靠快取,離線了還能正常跑,而且秒開。我把以前原生寫的小遊戲遷移到react,再遷移到webpack+react,最後再升級到pwa。具體介紹不多說,我們開始擼吧。
1.webpack
webpack攻略有很多,不囉嗦了,簡單介紹一些重點。記住幾個點:入口entry、出口output、外掛plugins、模組載入器loader。接下來你一個完整的專案的相關操作至少要包含這些。
還有一個就是path模組,專門讀取路徑的,做一切的配置前,首先把路徑搞好吧:
//一般我們就是這樣子做的
var path = require("path");
var ROOT_PATH = path.resolve(__dirname);//當前主入口目錄
var SRC_PATH = path.resolve(ROOT_PATH,"src");//src,你寫的程式碼在這裡
var DIST_PATH = path.resolve(ROOT_PATH,"dist");//打包結果
var COMP_PATH = path.resolve(SRC_PATH,"component");//vue、react都有的component
//然後我們的配置裡面
var config = {
mode:'development',
entry: path.resolve(__dirname, './src/index.js'),//webpack把主入口html變成js,然後注入html
output:{
path:DIST_PATH,
filename:"bundle.js"
},
}
複製程式碼
模組載入器,一般我們不用前處理器的話,繼續在config裡面新增配置,這樣子就基本滿足需求
module:{
rules:[
{
test:/\.(es6|js)$/,//考慮到es6
use:[
{
loader:"babel-loader",
}
],
exclude:/node_modules/ //不把nodemodules考慮進去
},
{
test:/\.(css)$/,
use:[
{
loader:"style-loader"
},
{
loader:"css-loader"
}
],
exclude:/node_modules/
},
{
test:/\.(png|jpeg|jpg|gif)$/,
use:[
{
loader:"url-loader",
}
],
exclude:/node_modules/
}
]
}
複製程式碼
對於外掛,我們一般就用htmlWebpackPlugin和熱更新就差不多了
plugins:[
new webpack.HotModuleReplacementPlugin(),
new htmlWebpackPlugin({
title: 'game',
template: path.resolve(__dirname, './index.html'),
//bunld.js會注入裡面
inject: true
}),
new OfflinePlugin() //這是pwa用的,等下講到
]
複製程式碼
還有一個伺服器:
var server = new WebpackDevServer(webpack(config), {
contentBase: path.resolve(__dirname, './dist'), //預設會以根資料夾提供本地伺服器,這裡指定資料夾
historyApiFallback: true, //這是history路由,如果設定為true,所有的跳轉將指向index.html
port: 9090, //預設8080
publicPath: "/", //本地伺服器所載入的頁面所在的目錄
hot: true, //熱更新
inline: true, //實時重新整理
historyApiFallback: true //不跳轉
});
server.listen(9090, 'localhost', function (err) {
if (err) throw err
})
複製程式碼
哦,對了,列舉一下require清單和package.json:
var webpack = require("webpack");
var path = require("path");
var htmlWebpackPlugin = require("html-webpack-plugin");
var webpackDelPlugin = require("webpack-del-plugin");
var WebpackDevServer = require('webpack-dev-server');
var ROOT_PATH = path.resolve(__dirname);
var SRC_PATH = path.resolve(ROOT_PATH,"src");
var DIST_PATH = path.resolve(ROOT_PATH,"dist");
var TEM_PATH = path.resolve(SRC_PATH,"component");
var OfflinePlugin = require('offline-plugin')
//package.json
{
"name": "pwawebpack",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"jquery": "^3.3.1",
"react-scripts": "1.1.1"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-plugin-react-transform": "^3.0.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-react": "^6.24.1",
"babel-preset-react-hmre": "^1.1.1",
"css-loader": "^0.28.11",
"file-loader": "^1.1.11",
"html-webpack-plugin": "^3.2.0",
"offline-plugin": "^5.0.3",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-transform-hmr": "^1.0.4",
"style-loader": "^0.21.0",
"url-loader": "^1.0.1",
"webpack": "^4.8.3",
"webpack-cli": "^2.1.3",
"webpack-del-plugin": "0.0.2",
"webpack-dev-server": "^3.1.4",
"webpack-notifier": "^1.6.0"
},
"scripts": {
"dev": "webpack-dev-server --inline --progress --config webpack.config.js"
}
}
複製程式碼
為了快點看到pwa+webpack的效果,那我們eslint、test就不寫了
2.pwa
我們就拿百度到的那些例子說吧,一個正常的pwa,由index.html、一個css、一個manifest.json、一個sw.js。我們要啟動一個pwa,這是必備的。 其實,是不是看起來有點像谷歌瀏覽器的擴充套件?有沒有試過自己寫谷歌瀏覽器外掛,比如遮蔽廣告的、個人工具的、某些網站收藏夾等等外掛。畢竟一家人,所以看起來也有點像。
html:
<head>
<title>PWA</title>
<meta name="viewport" content="width=device-width, user-scalable=no" />
<link rel="stylesheet" type="text/css" href="main.css">
</head>
<body>
<h1>1</h1>
</body>
複製程式碼
manifest.json:其實和自己寫的瀏覽器擴充套件差不多,就是一些關於名字、樣式、logo的配置
{
"name": "PWA",
"short_name": "p",
"display": "standalone",
"start_url": "/",
"theme_color": "#0000ff",
"background_color": "#00ff00",
"icons": [
{
"src": "logo.png",
"sizes": "256x256",
"type": "image/png"
}
]
}
複製程式碼
sw具體介紹 點這裡 生命週期的話,也不多說了,幾個階段:解析Parsed、安裝Installed、啟用Activated,中間失敗的話直接跳到廢棄Redundant階段,然後我們監聽這些事件,我們直接看效果。
var cacheStorageKey = 'v1'
var cacheList = [
'/',
"index.html",
"main.css",
"logo.png"
]
self.addEventListener('install', e => {
e.waitUntil(
caches.open(cacheStorageKey)
.then(cache => cache.addAll(cacheList))
.then(() => self.skipWaiting())
)
})
self.addEventListener('fetch', function(e) {
e.respondWith(
caches.match(e.request).then(function(response) {
if (response != null) {
return response
}
return fetch(e.request.url)
})
)
})
self.addEventListener('activate', function(e) {
e.waitUntil(
Promise.all(
caches.keys().then(cacheNames => {
return cacheNames.filter(name =>
name !== cacheStorageKey
).map(name=>caches.delete(name))
})
).then(() => {
return self.clients.claim()
})
)
})
複製程式碼
注意了,pwa需要https或者localhost,因為這東西能把你本地的檔案都拉取了,那也有可能幹其他事情,所以必須是要在安全的情況下跑的。還有,是不是發現改了html、js檔案,清空快取都不更新呢?其實改一下sw就可以了,manifest做應用快取也是,改個版本號,或者加個空格就行。
3.基於webpack的pwa
文件見官網
我們不用配置就可以跑起來,但是配置裡面有些地方需要注意的而且不能亂改,自行看文件。配置常用的是:caches(預設全部快取,也可以自己設定),externals(陣列形式,表示其他資源如cdn),excludes(陣列形式,除了哪些不能被快取),autoUpdate(多久後更新,預設一小時)
我們使用offline-plugin這個外掛,只需要在外掛裡面直接引入即可:
plugins: [
// ... 其他外掛
new OfflinePlugin()
]
複製程式碼
接著在我們的入口檔案index.js加入:
import * as OfflinePluginRuntime from 'offline-plugin/runtime';
OfflinePluginRuntime.install();
複製程式碼
這樣子直接跑webpack就ok了,試一下谷歌瀏覽器offline模式,你會發現還是能跑: