ES6 實戰

語言: CN / TW / HK

theme: channing-cyan highlight: night-owl


這是我參與11月更文挑戰的第17天,活動詳情檢視:[2021最後一次更文挑戰](https://juejin.cn/post/7023643374569816095/ "https://juejin.cn/post/7023643374569816095/") > TIP 👉 **苟利國家生死以,豈因禍福避趨之!____林則徐《赴戍登程口占示家人·其二》**

前言

# 05.01 【講義】ES6 專案實戰 - ES6 module - 併發請求 - class - 裝飾器 - 請求的封裝 - express 簡單中介軟體處理 # ES6 module 與 CommonJs的區別 ```js // util.js export let token = null export const getToken = () => { console.log('get Token') } setTimeout(() => { token = 'token value' }, 1000) ``` ```js // import {token} from './util' const {token} = require('./util') console.log('token', token) setTimeout(() => { console.log('token', token) }, 2000) ``` ## tree-shaking tree-shaking:不同,ES6 不裝載不需要的模組,webpack production模式使用tree-shaking直接刪除;module.exports匯出的是一個整體的物件 require也是一個整體的物件 注意:export default 物件被 import 後,掛在 default 上的屬性和方法,即使沒有被呼叫,也無法被 tree-shaking,所以我們在組織模組檔案時,應當儘可能避免 export default {A, B, C} 的寫法。export const xxx = {} 同理。 ```js export default { request() { console.log('request') }, response() { console.log('response') } } ``` ```js import util from './util' console.log(util.request) ``` ### vue2與3寫法的變化 ```js // 2.x import Vue from 'vue' Vue.nextTick(() => { // 一些和DOM有關的東西 }) // vue2 匯出 https://github.com/vuejs/vue/blob/dev/src/core/index.js // export default Vue // 3.x import { nextTick } from 'vue' nextTick(() => { // ... }) // vue3 匯出 https://github.com/vuejs/vue-next/blob/master/packages/vue/src/index.ts // export { compileToFunction as compile } // export * from '@vue/runtime-dom' ``` ## 值的引用 or 值的拷貝 module.exports require 如果修改了變數,則require中的不變 ## ES6 module 與 CommonJs的用法 ### module.exports require ```js module.exports = { } module.exports.xxx = '' module = {} // wrong,其他模組require後獲取不到這裡的匯出的 ``` ### export import ```js export const request = xxx or const request = 'xxx' export { request } ``` # http模組的封裝 ## 獲取token ```js // util.js import axios from "axios"; export let token = null let getTokenPromise = null export const getToken = () => { if(!getTokenPromise) { getTokenPromise = axios.get('/api/token').then(res => { token = res.data.token return token }) } return getTokenPromise; } ``` ## 請求頭新增token // http.js ```js import axios from 'axios' import {token, getToken} from './util' const instance = axios.create({ baseURL: '/api' }) instance.interceptors.request.use(async config => { let headerToken = token if(headerToken) { console.log('token已存在', headerToken) } else { console.log('token不存在發起請求') headerToken = await getToken() } config.headers.token = headerToken return config }) instance.interceptors.response.use(res => res.data) export const getTabs = () => instance.get('/tabs') export const getNewsList = () => instance.get('/news/list') ``` ## 服務端驗證token ```js app.use('/api', function(req, res, next) { if(req.path === '/token' || req.headers.token === '42gd2') { next() } else { next('invalid token') } }) // ... app.use('/api', function(err, req, res, next) { res.send({ error: 1, message: err }) }) ``` ## 客戶端併發請求 ```js // Promise.all const [tabs, newsList] = await Promise.all([getTabs(), getNewsList()]) // or await const tabsTask = getTabs() const newsTask = getNewsList() const tabs = await tabsTask const newsList = await newsTask ``` ## 避免傳送重複請求 ```js import axios from 'axios' const peddingMap = new Map() export const addPeddingRequest = (config) => { const {url, method} = config const mapKey = [url, method].join('&') if(peddingMap.has(mapKey)) { const cancel = peddingMap.get(mapKey) cancel(mapKey) peddingMap.delete(mapKey) } config.cancelToken = new axios.CancelToken(cancel => { peddingMap.set(mapKey, cancel) }) } export const deletePeddingRequest = (config) => { const {url, method} = config const mapKey = [url, method].join('&') peddingMap.delete(mapKey) } // http.js instance.interceptors.response.use(res => { deletePeddingRequest(res.config) return res.data }, error => { if(axios.isCancel(error)) { console.error('此請求被取消', error) } return Promise.reject(error) }) ``` # App ```js class App { constructor({root}) { this.root = root; this.init() } init() { this.requestData() } async requestData() { // const [tabs, newsList] = await Promise.all([getTabs(), getNewsList()]) const tabsTask = getTabs() const newsTask = getNewsList() const tabs = await tabsTask const newsList = await newsTask console.log(tabs, newsList) new Tab(tabs).mount(this.root) new NewsList(newsList).mount(this.root) } } new App({root: document.body}) ``` # Component ```js export default class Component { constructor(data) { this.props = { data, } } constructElement() { const html = this.render() const $container = document.createElement('div') $container.innerHTML = html this.$el = $container.firstChild } mount($container) { if(!this.$el) { this.constructElement() } $container.appendChild(this.$el) } render() { return null } } ``` ## 實現新聞列表 ```js import Component from './component' export default class NewsList extends Component { render() { const {data: newsList} = this.props.data return( `
    ${newsList.map(news => { return ( `
  • ${news.title}

  • ` ) }).join('')}
` ) } } ``` ### 新聞列表滾動載入 ```js listenScroll() { const DISTANCE = 100 window.addEventListener('scroll', () => { const scrollY = window.scrollY const screenHeight = window.screen.height const domHeight = document.documentElement.offsetHeight if(domHeight - (scrollY + screenHeight) < DISTANCE) { this.appendList() } }) } async appendList() { console.log('appendlist') const newsList = await getNewsList() new NewsList(newsList).mount(this.root) } ``` ### 防抖與節流的區別 以時間為3秒為例 防抖:關鍵詞——等待,等待3秒內沒有新的action,就執行 節流:關鍵詞——丟棄,三秒內的action只執行一次,其餘的action會被丟棄 ### 普通節流 ```js // util.js export function throttle(func, timeout = 1000) { let done = false return (...args) => { if(!done) { func.call(this, ...args) done = true setTimeout(() => { done = false }, timeout) } } } // index.js constructor({root}) { this.root = root; this.init() this.appendList = throttle.call(this, this.appendList, 500) } ``` ### 裝飾器實現節流 ```js // util.js export const decoratorThrottle = (timeout = 1000) => { return (targetPrototype, propName) => { const oldMethod = targetPrototype[propName] let lastActionTime = 0 targetPrototype[propName] = function(...args) { const currentTime = Date.now() if(currentTime - lastActionTime > timeout) { oldMethod.call(this, ...args) lastActionTime = currentTime } } return targetPrototype } } // index.js @decoratorThrottle(500) async appendList() { console.log('appendlist') const newsList = await getNewsList() new NewsList(newsList).mount(this.root) } ``` 「歡迎在評論區討論」 #### 希望看完的朋友可以給個贊,鼓勵一下