[JS Advanced] Научу вас играть в ES6 в средней и фоновой системах.

JavaScript

Оригинальная ссылка:GitHub.com/осень-зима/…

Эта статья представляет собой статью, ориентированную на приложения, в которой обобщаются различные варианты использования ES6 в соответствии с реальными сценариями проекта. В этой статье не будет слишком подробно объясняться особенности или принципы синтаксиса ES6, а основное внимание будет уделено пониманию нового синтаксиса и новых функций ES6 из реальных сценариев приложений.

1. Объявление переменной

Чтобы решить (или стандартизировать) проблему области видимости на уровне блоков в JS, ES6 добавилletа такжеconstЕсть два способа объявить переменные, иvarРазница в следующем:

  • varОбъявленная переменная, область действия будет расширена, и переменная может быть переназначена;
  • letа такжеconstОбъявленная переменная имеет объем уровня блока и временную мертвую зону, но без переменной продвижения;
  • letОбъявленная переменная может быть переназначена,constнет.

В нашей ежедневной разработке проектов мы будем использовать помощь IDE при сохранении файлов или отправке Git.EslintИнструменты, подобные этому, проверяют спецификации кода.Одним из распространенных запросов проверки является:声明的变量xxx没有被再赋值,建议使用const. Поэтому обычно, когда мы объявляем переменные с помощью ES6, мы можем следовать следующим принципам:

  • Переменные, которые необходимо переназначить, используйтеletЗаказ;
  • Для переменных, которые не будут переназначаться, используйтеconstЗаказ;
  • отказаться от использованияvarЗаказ.
function submit(){
  let loading = true
  const postData = this.getParamData()
  // ...
  loading = false
}

2. Разрушающее назначение

Деструктурирующее присваивание — это процесс извлечения значений из строк, массивов или объектов и присвоения значений переменным. Его можно использовать в определениях переменных, параметрах функций и других сценариях.

// 解构数组
let [name, age, sex='male'] = ['安歌','18']
// output -> name: '安歌', age: 18, sex='male'

// 对象解构
const ange = {name: '安歌', sex: 'male'}
let {
  name,
  age=18,
  sex
} = ange
// output -> name: '安歌', age: 18, sex: 'male'

// 字符串解构
const hi = 'hello'
let [a, b, c, d, e] = hi
let { length } = hi
// output -> a: 'h', b: 'e' ... length: 5 

В-третьих, структура данных Set и Map

SetСтруктура данных требует, чтобы ее элементы были неповторяемыми, и эта уникальная особенность может значительно облегчить разработку проекта;MapСтруктура данных — это набор пар ключ-значение, который обеспечивает более широкую поддержку ключевых типов данных и имеет хороший набор API.

Здесь мы берем список данных в качестве сценария: сверка заказов на продажу, требующая, чтобы сверка ограничивалась только одним и тем же продуктом.

1. Неповторяемость набора

// 根据Set内元素不重复的特性,判断申请对账按钮是否可用
function canSubmit() {
  const checkedListData = getCheckedList() // 获取选中的数据项列表
  const productNos = new Set( // 取出商品ID列表
    checkedListData.map(each => each.product_no)
  )
  return productNos.size === 1
}

2. Хранение картографических данных

/*
 * 使用Map数据结构存储勾选数据列表
 * 假设列表数据项 item={ id, order_no, product_no, product_name, isChecked }
 */
const checkedData = new Map()
// 选中操作
checkedData.set(item.id, item)

// 取消选中
checkedData.delete(item.id)

// 清空
checkedData.clear()

// 取出选中项ID集合
const recordIds = checkedData.keys()

// 如果需要对选中的数据进行二次筛选,可以取出数据集合
const data = checkedData.values()

// 从其他页码(比如第2页)返回到已访问过的页面(比如第一页),一般需要还原用户的选中状态
checkedData.has(item.id) && (item.isChecked = true)

В-четвертых, строка шаблона

Строки шаблона ES6 позволяют нам встраивать переменные в строку или определять многострочную строку для лучшей читабельности.

const orderNo = 'ON90509000001'
const msg = `订单${orderNo}对账失败!`
// output -> 订单ON90509000001对账失败!

5. Основные функции

1. Стрелочные функции

Стрелочные функции делают ваши выражения более краткими. Он имеет следующие особенности:

  • внутри функцииthis, это контекст, в котором определена стрелочная функцияthis, который неизменен. но черезbind,call,applyметод изменения контекстаthisнаправление;
  • Это не конструктор, т.е. его нельзя использоватьnewЗаказ;
  • не существует в теле функцииargumentsОбъект, но может быть использованrestвместо этого параметр;
  • нельзя использоватьyieldкоманда, поэтому функции стрелок нельзя использовать в качествеGeneratorфункция.
const obj = {
  name: '安歌',
  hello: function () {
    setTimeout(() => console.log(this.name), 1000)
  }
}
obj.hello() // output -> '安歌'
obj.hello.call({name: 'Ange'}) // output -> 'Ange'

2. Параметры по умолчанию

function add(x, y, fixed=2) {
  const result = (+x) + (+y)
  return result.toFixed(fixed)
}

add(1,2) // output -> '3.00'

3. остальные параметры

function add(...args) {
  let sum = 0
  for (var val of args) {
    sum += val
  }
  return sum
}

add(2, 5, 3) // 10

4. Деструктуризация параметров

function submit() {
  this.$post('/login').then( ({code, data}) => {} )
}

6. Условное решение

Управление разрешениями является важной функцией в промежуточной и фоновой системах, от управления правами доступа к странице до полномочий операций определенной функции. Здесь управление разрешениями используется в качестве сценария приложения, чтобы увидеть, как использовать ES6 для элегантного выполнения условного суждения.

1. Контроль разрешений маршрутизации

function hasPer(route) {
  const { permissions=[] } = userInfo // 获取用户权限列表
  const routePer = route.meta.per // 预先为每个路由配置的访问权限
  return permissions.includes(routePer)
}

2. Контроль разрешений системных функций (например, операция согласования)

function checkPer(per) {
  const { permissions=[] } = userInfo // 获取用户权限列表
  return Array.isArray(per) ? 
    // per是个列表,permissions需要包含per里面要求的所有权限
    per.every(each => permissions.includes(each)) : 
    (
      // per是个正则,permissions需要存在某个权限通过per的正则验证
      per instanceof RegExp ? permissions.some(each => per.test(each)) : 
      // per是个字符串,
      permissions.includes(per)
    )
}

Семь, перепишите цикл for

Предположим, у нас есть список данных заказа:

const orderList = [
  {oid: 'ON0001', pid: 'PN0001', pname: '蘸酱短袖', clinet: '杨过', total: 100, date: '2019-07-10'},
  {oid: 'ON0002', pid: 'PN0002', pname: '椒盐短袖', clinet: '小龙女', total: 122, date: '2019-07-10'},
  {oid: 'ON0003', pid: 'PN0001', pname: '蘸酱短袖', clinet: '郭襄', total: 100, date: '2019-07-14'},
  {oid: 'ON0004', pid: 'PN0003', pname: '炭炙短袖', clinet: '郭襄', total: 137 , date: '2019-07-18'},
  {oid: 'ON0005', pid: 'PN0001', pname: '蘸酱短袖', clinet: '小龙女', total: 100, date: '2019-07-20'}
]

1. Составьте карту данных

// 取出列表中的所有订单号
const oIds = orderList.map(each => each.oid)

2. forEach изменяет исходные данные

// 给列表每个订单加一个isCP属性,默认值为false
orderList.forEach(each => each.isCP = false)

3. Как продолжить

Если клиентами-покупателями являются Ян Го и Сяолуннв, идентифицируйте их как пару CP (другими словами, зацикливайте процесс списка и выполняйте его, когда встречается Го Сян).continueЗаказ):

orderList.forEach(each => ['杨过', '小龙女'].includes(each.clinet) && (each.isCP = true))

4. Как разбить

Продавец занимается преимущественной деятельностью и последовательно вычитает 5 юаней из общей стоимости каждого заказа (вычтенные 5 юаней возвращаются покупателю) до тех пор, пока не встретится первый заказ с нечетной общей стоимостью (другими словами, в фантомном процесс списка, первая нечетная общая цена будет выполненаbreakкоманда для завершения цикла).

orderList.some(each => each.total % 2 ? true : each.total -= 5)

// 可以验证,当循环到{oid: 'ON0004', pid: 'PN0003', pname: '炭炙短袖', clinet: '郭襄', total: 137 , date: '2019-07-18'},这条数据之后,因为结果返回true,循环将结束,即后面的数据不会再被循环。

Восемь, преобразование объектов и массивов

Сценарий «что, если». Добавьте стиль статуса к согласованию каждого заказа (success,warning,danger), со следующими данными:

// 状态码说明: 100->对账成功、101->对账中、102->审核中、103->对账失败、104->取消对账
// 规定有如下样式映射表: 
const style2StatusMap = {
  success: [100],
  warning: [101,102],
  danger: [103,104]
}

Функция реализации: преобразование таблицы сопоставления стилей в таблицу сопоставления состояний, например:

const status2styleMap = {
  100: 'success',
  101: 'warning',
  102: 'warning',
  103: 'danger',
  104: 'danger'
}

Используйте ES6entriesа такжеreduceи т.д. Реализация API:

// 实现toPairs函数取出Map的键值对,函数返回形如[[key1,val1]...]的数组
const toPairs = (obj) => Object.entries(obj)
// 实现head/last函数取出列表头尾元素
const head = list => list[0]
const last = list => list.reverse()[0]

// 将对象转换为数组
const pairs = toPairs(style2StatusMap)
// 再将数组转化为对象
const status2styleMap = pairs.reduce((acc, curr) => {
  const style = head(curr)
  const status = last(curr)
  return status.reduce((accer, each) => (accer[each] = style,accer), acc)
}, {})

// output -> {100: "success", 101: "warning", 102: "warning", 103: "danger", 104: "danger"}

9. Сбор данных

Иногда нам нужно вынести часть элемента данных в объект данных, а затем передать его компоненту или отправить на сервер после определенной сборки.

Сценарий «что, если».formПеременные хранят данные формы,routerс некоторыми параметрамиqueryData. Реализация повторно собирает измененные данные и отправляет их на сервер, но некоторые данные отправлять не нужно.

// 基础数据
let form = { name: '账单名', no: '对账单编号', oNo: '订单编号', pNo: '商品编号', pName: '商品名'@, pNum: '商品数量', applicant: 'ange@gmail.com(安歌)', ... }
const query= { id: '对账单ID'}

1. Object.assign данные извлечения

// 先覆写applicant参数
Object.assign(form, {
  applicant: form.applicant.split('(')[0]
  // ...其他可能需要覆写或追加的参数
}

2. фильтровать данные фильтра

// 采集出需要的数据项
const exclude = ['pName', 'pPrice']
const formData = Object.keys(form)
  .filter(each => !exclude.includes(each))
  .reduce((acc, key) => (acc[key] = form[key], acc), {}

3. Оператор распространения для объединения данных

// 使用扩展运算符组合form和query的参数
const postData = {
  ...formData,
  ...query
}

10. Асинхронные функции

ES6 обеспечиваетPromiseобъект иasyncФункции используются для обработки асинхронных операций, т.httpпросилaxiosбиблиотека основана наPromiseосуществленный. использоватьPromiseсвойства, мы можемhttpВозвращаемое содержимое запроса перехватывается без уведомления разработчика.

Сценарий приложения: Интерфейсы промежуточной и фоновой систем обычно имеют строгие требования к разрешениям, и нам необходимо фиксировать исключения интерфейса. Перехватывая и инкапсулируя, вы можете выполнить уровень проверки до того, как код бизнес-уровня получит данные.

1. Переупаковать пост на основе Promise

// 根据需要自定义创建Axios对象实例
const axios = new Axios.create() 
// 封装post函数
const $post = (url, postData) => {
  return axios.post(url, postData) // 在未封装的情况下,我们一般通过这种方式发起一个post请求
    .then(interceptors.bind(this)) // 针对权限验证的拦截
    .catch(reject.bind(this)) // 捕获接口异常
}

// 业务层调用
$post('./get_order_list', postData).then(res => {})

2. Перехватчик перехватчиков

// 预处理后端错误码
const interceptors = (res) => {
  const code = res.data.code
  if(code === 100) { // 未登录
    console.log('请先登录') // or redirect to login_error_url
    return Promise.reject('LoginError')
  } else if (code === 101) { // 缺失访问/修改/删除权限
    console.log('没有访问权限') // or redirect to permission_error_url
    return Promise.reject('PermissionError')
  } else { // 正常将数据返回
    return response.data
  }
}

3. Отказ от функции перехвата исключений

// 针对接口异常的捕获
const reject = (error) => {
  if(error instanceof Error) {
        const errMsg = error.message
        let type = 'error'
        let message = '服务器出错了',请联系管理员
        if(/timeout/.test(errMsg)) { // 服务器请求超时
            message = '请求超时...'
            type = 'warning'
        }
        // ...
        console.log(message)
    }
   return Promise.reject(error)
}

Эффект следующий:

4. Асинхронные функции делают асинхронный код более элегантным и лаконичным.

// 配合await关键字一起使用
const submit = async () => {
  const data = await $post(url)
  // ...
}

// 也可以用在Vue等框架的钩子函数中
async mounted () {
  const data = await $post(url)
  // ...
}

Одиннадцать, динамическая загрузка (импорт)

В системе могут быть некоторые функции (типа печати), требующие внедрения сторонних библиотек (html2canvas.js,print.jsи т. д.), но иногда некоторые сторонние библиотеки могут быть огромными по размеру, и пользователи, посещающие страницу, могут не обязательно запускать функцию, в этом случае можно рассмотреть динамическое внедрение.

function print () {
  import('html2canvas.js').then((html2pdf) => {
    html2pdf().outputPdf()
  })
}

import()вернутьPromiseОбъект, который загружает указанный модуль только во время выполнения. Другой распространенный сценарий применения:VueМаршруты загружаются лениво.

const productListModule = () => import(/* webpackChunkName: "product" */  'view/product/List')  // 商品列表

Двенадцать, многомерная комбинированная сортировка

Предполагаемый сценарий: на странице со списком продуктов есть два сортируемых столбца: цена за единицу (price) и стили (style). Если сначала щелкнуть Сортировать по цене за единицу, а затем Сортировать по стилю, стили должны быть отсортированы в соответствии с порядком цены за единицу.

// 基础数据
const products = [
    { name: "椒盐T恤", price: 3, style: 'Japanese' },
    { name: "蘸酱短袖", price: 5, style: 'Chinese' },
    { name: "碳炙短袖", price: 4, style: 'Chinese' },
    { name: "印花短袖", price: 8, style: 'England' },
    { name: "写意短袖", price: 3, style: 'Chinese' },
    { name: "清蒸T恤", price: 4, style: 'Japanese' },
]

// 定义不同的排序规则
const byPrice = (a, b) => a.price - b.price
const byStyle = (a, b) => a.style > b.style ? 1 : a.style === b.style ? 0 : -1

// 利用reduce组合排序
const sortByFlattened = fns => (a,b) => 
    fns.reduce((acc, fn) => acc || fn(a,b), 0)

// 组合后的排序函数,排序优先级按数组内元素位置编号
const byPriceStyle = sortByFlattened([byPrice,byStyle])

console.log(products.sort(byPriceStyle))

Результаты сортировки следующие: сначала по цене в порядке возрастания, затем по стилю в порядке возрастания.

Эпилог

Вышеприведенное является лишь кратким описанием использования ES6 в повседневной разработке отдельными лицами. Оно не включает все функции ES6. Есть еще много функций, которые используются реже.Приглашаем всех общаться и делиться применением ES6 в вашем реальном проекте..

Наконец, если вы считаете, что эта статья вдохновила вас, пожалуйста, не скупитесь на лайки, ха-ха~