preface
As mentioned in the previous article, scheme 1 has a disadvantage. The scalability of obtaining hash value is not high, and it needs to be obtained according to the actual situation of different projects. And if the rest of the project doesn’t know this logic and inserts a script tag in front of your value after packaging, it’s a “cool” one, and the user will be constantly notified of version updates.
Train of thought
- When the package is complete, a script is automatically executed to insert a meta tag into the head tag to record the release version number.
- The system checks whether the current version is the same as the latest version. If the current version is not the same as the latest version, a message is displayed indicating that the latest version is updated.
implementation
1. Create a script
- Create a version.js file at the root of the project as follows
const fs = require('fs'); */ const versionNo = new Date().gettime (); const htmlPath = './build/index.html'; const insertVersionMeta = () => { let html = fs.readFileSync(htmlPath, 'utf-8'); html = html.replace('<head>', `<head><meta name="version_no" content="${versionNo}" />`); fs.writeFileSync(htmlPath, html); }; insertVersionMeta();Copy the code
- Execute the script
- Add a custom command to package.json to execute the node script and automatically execute it after the build is complete
{... Cross-env NODE_ENV=production React-app-rewired build && NPM run version "node ./version.js" ... (Omit other configuration)}Copy the code
This automatically inserts version information into the HTML after each packaging.
3. Check whether the current version number is consistent with the latest version number (in fact, the logic here is much the same as the last article)
- Gets the current version number
const getCurrentVersion = useCallback(() => { let version = ''; const metaList = document.querySelectorAll('meta'); if (metaList.length) { metaList.forEach((item) => { if (item.name === 'version_no') { version = item.content; }}); } curVersion.current = version; } []);Copy the code
- Obtain the latest version number
*/ const fetchNewVersion = useCallback(async () => {const timestamp = new Date().getTime(); const response = await axios.get(`${window.location.origin}? time=${timestamp}`); // Return a string that needs to be converted to HTML const el = document.createElement(' HTML '); el.innerHTML = response.data; let newVersion = ''; Const metaList = el.querySelectorAll('meta'); if (metaList.length) { metaList.forEach((item) => { if (item.name === 'version_no') { newVersion = item.content; }}); } console.log('fetchNewVersion', curVersion.current, newVersion); if (newVersion && newVersion ! == curVersion.current && ! Visible) {setVisible(true); } else if (newVersion && newVersion === curVersion.current && visible) { setVisible(false); } }, [visible]);Copy the code
Next is the trigger timing. My idea is to encapsulate it as a component and mount it under the project’s follower node. When the project is initialized, an update is triggered and polling is enabled (half an hour/an hour) to clear the poll if the user leaves or the screen goes off. Lighting up the screen or switching back to the TAB on which the web page is located triggers detection and polling is enabled.
4. Prompt the user to update
After the user clicks refresh, the browser automatically refreshes and closes the prompt.
Const handleConfirm = useCallback(() => {setVisible(false); window.location.reload(); } []);Copy the code
Write in the last
Compared with scheme 1, this scheme has higher scalability and does not require more attention to the structure of HTML. I recommend this scheme. In fact, the process of development, the difficult is a good program, the program is actually half the success of development.
- The complete code
import React, { useState, useRef, useEffect, useCallback, useMemo, memo } from 'react'; import axios from 'axios'; import LionetConfirm from '@/shared/components/LionetConfirm'; import { setTrackerUserId } from './Tracker'; /** Check for update */ const VersionUpdateToast = () => {/** Current version */ const curVersion = useRef<any>(null); /** timer */ const timer = useRef<any>(null); const [visible, setVisible] = useState(false); Const pollingTime = useMemo(() => {return 30 * 60 * 1000; } []); */ const fetchNewVersion = useCallback(async () => {// requesting home page address in JS does not update page const timestamp = new Date().getTime(); const response = await axios.get(`${window.location.origin}? time=${timestamp}`); // Return a string that needs to be converted to HTML const el = document.createElement(' HTML '); el.innerHTML = response.data; let newVersion = ''; Const metaList = el.querySelectorAll('meta'); if (metaList.length) { metaList.forEach((item) => { if (item.name === 'version_no') { newVersion = item.content; }}); } console.log('fetchNewVersion', curVersion.current, newVersion); if (newVersion && newVersion ! == curVersion.current && ! Visible) {setVisible(true); } else if (newVersion && newVersion === curVersion.current && visible) { setVisible(false); } }, [visible]); /** Start timer */ const setTimer = useCallback(() => {timer.current = setInterval(fetchNewVersion, pollingTime); }, [fetchNewVersion, pollingTime]); / / const clearTimer = useCallback(() => {if (timer.current) {clearInterval(timer.current); timer.current = null; }} []); Const getCurrentVersion = useCallback(() => {let version = "; const metaList = document.querySelectorAll('meta'); if (metaList.length) { metaList.forEach((item) => { if (item.name === 'version_no') { version = item.content; }}); } curVersion.current = version; } []); Const getVersion = useCallback(() => {getCurrentVersion(); fetchNewVersion(); }, [fetchNewVersion, getCurrentVersion]); // const onVisibilityChange = useCallback(() => {// eslint-disable-next-line prefer-destructuring if (! document.hidden) { setTimer(); getVersion(); } else { clearTimer(); } }, [clearTimer, getVersion, setTimer]); useEffect(() => { getVersion(); setTimer(); return () => { clearTimer(); }; } []); useEffect(() => { document.addEventListener('visibilitychange', onVisibilityChange); return () => { document.removeEventListener('visibilitychange', onVisibilityChange); }; } []); Const handleConfirm = useCallback(() => {setVisible(false); setTrackerUserId(); window.location.reload(); } []); Return (<LionetConfirm visible={visible} isShowCancelBtn={false} confirmText=" refresh "onConfirm={handleConfirm} > <span> </LionetConfirm>); }; export default memo(VersionUpdateToast);Copy the code