Fork me on GitHub

面试中的复杂度分析

# 面试中的复杂度分析

很多同学一提起复杂度分析就头疼,马上想起了《算法导论》中复杂的数学推导。但其实在一般的企业面试中,对复杂度的分析要求并没有那么高,但也是绕不过去的坎儿。在这一章,和大家介绍一下,面试中需要掌握的复杂度分析。…

# 2-1 究竟什么是大 O(Big O)

# 大 O?

  • n 表示数据规模
  • O(f (n))表示运行算法所需要执行的指令数,和 f (n) 成正比

# 例如

  • 二分查找法 O (logn) — 所需执行指令数 :a * logn
  • 寻找 数组中的最大 / 最小值 O (n) — 所需执行指令数 :b * n
  • 归并排序算法 O (nlogn) — 所需执行指令数 :c * nlogn
  • 选择排序法 O (n^2) — 所需执行指令数 :d * n^2

# 到底什么是 Big O?

mark

mark

在学术界,严格来讲,O (f (n)) 表示算法执行的上界

归并排序算法的时间复杂度是 O (nlogn) 的,同时也是 O(n^2)

在业界,我们就使用 O 来表示算法执行的最低上界

我们一般不会说归并排序是 O (n^2) 的

# 例子

  • O(nlogn + n)= O (nlogn)
  • O(nlogn + n2)=O(n2)

# 无法判断

  • O(AlogA + B) – A 与 B 无法确定

  • O(AlogA + B ^2)

  • 对邻接表实现的图进行遍历

    • 时间复杂度:O( V + E )

# 一个时间复杂度的问题

有一个字符串数组,将数组中的每一个字符串按照字母序排序;之后再将整个字符串数组按照字典序排序。整个操作的时间复杂度?

mark

正确解答:

  • 假设最长的字符串长度为 s ;数组中有 n 个字符串
  • 对每个字符串排序:O(slogs)
  • 将数组中的每一个字符串按照字母序排序:O(n * slog (s) )
  • 将整个字符串数组按照字典序排序:O(s * nlog (n) )

mark

# 算法复杂度在有些情况是用例相关的

  • 插入排序 O(n ^ 2)

    • 最差情况:O(n ^ 2)
    • 最好情况:O(n)
    • 平均情况:O(n ^ 2)
  • 快速排序算法 O (nlogn)

    • 最差情况:O(n ^ 2)
    • 最好情况:O( nlogn )
    • 平均情况:O( nlogn )

# 2-2 对数据规模有一个概念

# 抛出问题

对 10 ^ 5 的数据进行选择排序,结果计算机假死?

  • 如果要想在 1s 之内解决问题:
    • O(n ^ 2)的算法可以处理大约 10 ^ 4 级别的数据
    • O( n )的算法可以处理大约 10 ^ 8 级别的数据
    • O( nlogn )的算法可以处理大约 10 ^7 级别的数据

# 空间复杂度

  • 多开一个辅助的数组:O(n)
  • 多开一个辅助的二维数组:O(n ^ 2)
  • 多开常数空间:O(1)

mark

# 2-3 简单的复杂度分析

# O(1):

mark

# O( n ):

mark

mark

1/2 *n 次 swap 操作也是:O (n) 。

# O(n ^ 2): (选择排序)

mark

mark

并不是所有双重循环都是 O(n ^ 2):例如 【因为里面的循环次数是固定的 】

mark

下面算法是 O(logn)级别的: 【 自增并不是每次都加 1 】

mark

下面算法是 O( sqrt (n) )【 判断 n 是不是 一个素数】

mark

# O(logn):( 二分查找法 )

mark

mark

# 整形转成字符串

mark

# log 以 2 为底 和 以 10 为底有区别吗

mark

# 2-4 亲自试验自己算法的时间复杂度

# 复杂度实验

实验,观察趋势

每次 将数据规模提高两倍,看时间的变化

# 2-5 递归算法的复杂度分析

不是有递归的函数就一定是 O( nlogn )!

# 递归中进行一次递归调用的复杂度分析

二分法使用递归:

mark

mark

mark

mark

引申:上述 函数增加求 负次幂?

# 递归中进行多次递归调用

mark

mark

深度不一样,并不是所有 多次递归调用 的时间复杂度 为:O(2 ^ n)

mark

# 递归函数的时间复杂度

查阅 主定理 (面试一般不考察)

# 2-6 均摊时间复杂度分析(Amortized Time Analysis)

mark

# 2-7 避免复杂度的震荡

mark

mark

# 复杂度的震荡的解决方案

mark

算法面试到底是什么鬼

# 算法面试到底是什么鬼?

玩转算法面试 从真题到思维全面提升算法思维

为了面试,更为了提升你的算法思维

一提起算法面试,很多同学就会心有余悸。可其实,大多数企业的算法面试,并没有那么可怕。并不是一定要啃完整本《算法导论》,才能玩儿转算法面试;也并不是只有 ACM 参赛选手,才能笑傲算法面试。恰恰相反,大多数算法面试关注的算法思维,其实很基础。在这一章,和大家聊一聊,算法面试,到底是什么鬼?…

# 1-1 算法面试不仅仅是正确的回答问题

# 算法面试是什么?

  • 让大家在面对面试中的算法问题时,有一个合理的思考路径
    • 不代表能够 “正确” 回答每一个算法问题,但是合理的思考方向其实更重要,这也是正确完成算法面试问题的前提
    • 算法面试优秀不意味着技术面试优秀
    • 技术面试优秀不意味着能够拿到 Offer

# 引言

# 问题:对一组数据进行排序

不假思索思考方向:

  • 快速排序算法 O(nlogn)

正确的方式是:应该和面试官探讨( 思考路径 ):

  • 这组数据有什么样的特征?
    • 有没有可能包含有大量重复的元素?
    • 如果有这种可能的话,三路快排是更好的选择。
  • 这组数据有什么样的特征?
    • 是否大部分数据距离它正确的位置很近?是否近乎有序?
    • 如果是这样的话,插入排序是更好的选择
  • 这组数据有什么样的特征?
    • 是否数据的取值范围非常有限?比如对学生成绩排序
    • 如果是这样的话,计数排序是更好的选择
  • 对排序有什么额外的要求?
    • 是否需要稳定排序?
    • 如果是的话,归并排序是更好的选择
  • 数据的存储状况是怎么样的?
    • 是否是使用链表存储的?
    • 如果是的话,归并排序是更好的选择
  • 数据的存储状态是怎样的?
    • 数据的大小是否可以装载在内存里?
    • 数据量很大,或者内存很小,不足以装载在内存里,需要使用外排序算法

# 1-2 什么是 “正确” 的回答一个算法问题

正确 还包含对问题的独到见解;优化;代码规范;容错性

如果是 非常难的问题,对你的竞争对手来说,也是难的。

关键在于你所表达出的解决问题的思路

甚至通过表达解题思路的方向,得出结论:这个问题的解决方案,应该在哪一个领域,我可以通过查阅或者进一步学习解决问题

# 常见问题

  • 项目经历 和 项目中遇到的实际问题
  • 你遇到的印象最深的 bug 是什么?
  • 面向对象
  • 设计模式
  • 网络相关;安全相关;内存相关;并发相关
  • 系统设计;scalability

技术面试只是面试的一部分。面试不仅仅是考察你的技术水平,还是了解你的过去以及形成的思考行为方式

关于过去:参与项目至关重要

# 项目经历

  • 本科生
    • 毕业设计
    • 其它课程设计 (大作业,大一点的程序设计等等)
  • 如何找到项目?
    • 实习
    • 参与实战课程学习
      • 慕课网
      • Coursera
  • 创建自己的项目
    • 自己做小应用:计划表;备忘录;播放器…
    • 自己解决小问题:爬虫;数据分析;词频统计
    • “不是项目” 的项目:一本优秀的技术书籍的代码整理等…
    • 分享:自己的技术博客;github 等等…

# 行为类问题

通过过去了解你的思考行为方式?

  • 遇到的最大的挑战?
  • 犯过的错误?
  • 遭遇的失败?
  • 最享受的工作内容?
  • 遇到冲突的处理方式?
  • 做的最与众不同的事儿?

# 准备好合适的问题问面试官

  • 整个小组的大概运行模式是怎样的?
  • 整个项目的后续规划是如何的?
  • 这个产品中的某个问题是如何解决的?
  • 为什么会选择某些技术?标准?
  • 我对某个技术很感兴趣,在你的小组中我会有怎样的机会深入这种技术?

算法面试仍然是非常重要的一部分

# 1-3 如何准备算法面试

准备面试 和 准备算法面试 是两个概念

算法面试 ,只是面试中的一个环节

# 算法面试并没有那么难

  • 远远不需要啃完一本 《算法导论》
    • 过于强调理论证明
  • 高级数据结构 和 算法面试提及的概率很低
    • 红黑树
    • 计算几何
    • B - Tree
    • 数论
    • 斐波那契堆
    • FFT

算法面试远远不需要达到信息学竞赛的水平

mark

# 算法面试的准备范围

  • 不要轻视基础算法 和 数据结构,而只关注 “有意思” 的题目

重点关注:

  • 各种排序算法
  • 基础数据结构和算法的实现:如堆、二叉树、图…
  • 基础数据结构的使用:如链表、栈、队列、哈希表、图、Trie、并查集…
  • 基础算法:深度优先、广度优先、二分查找、递归…
  • 基本算法思想:递归、分治、回溯搜索、贪心、动态规划…

# 选择合适的 OJ

OJ:online judge

在线判题系统

mark

# 推荐

  • LeetCode ( 源于真实的面试问题 )
  • HackerRank ( 对问题分类很详细 ,更难一点,辅助作用)

注意: 在学习和实践做题之间,要掌握平衡

# 1-4 解决算法面试问题的整体思路

# 注意题目中的条件

  • 给定一个有序数组…
    • 有序: 是不是可以使用二分查找法
  • 有一些题目中的条件本质是暗示:
    • 设计一个 O(nlogn)的算法
      • 分治法
    • 无需考虑额外的空间
      • 开辟额外的空间
    • 数据规模大概是 10000
      • O(n 的二次方)

# 当没有思路的时候

  • 自己给自己几个简单的测试用例,试验一下
  • 不要忽视暴力解法。暴力解法通常是思考的起点

# 不要忽视暴力法

mark

mark

# 优化算法

# 无头绪的思路

  • 遍历常见的算法思路
  • 遍历常见的数据结构
  • 空间 和 时间的 交换 ( 哈希表 )
  • 预处理信息 ( 排序 )
  • 在瓶颈处寻找答案:O(nlogn)+ O(n ∧ 2);O(n∧3)

# 实际编写问题

  • 极端条件的判断
    • 数组为空?字符串为空?数量为 0? 指针为 NULL ?
  • 变量名
  • 模块化,复用性

组件化和 React

# 组件化和 React

本章先带领学生做一个 React 的实例,熟悉 React 开发环境、以及 组件化 的概念。然后,通过实例来讲解 React 的 实现原理,包括 JSX 的本质虚拟 DOM 和 JSX 的结合、以及 setState 。最后,对比 vue 和 React ,分析两者的异同。

组件化和 React 高级面试知识点

Think great thoughts and you will be great!

心怀伟大的理想,你将会变得伟大。

# 知识点

  • 是否做过 React 开发?
  • React 以及组件化的一些核心概念
  • 实现流程

# 题目

  • 说一下对组件化的理解
  • JSX 本质是什么?
  • JSX 和 vdom 的关系?
  • 说一下 setState 的过程
  • 阐述一下对 React 和 Vue 的 认识

# 回顾 React

  1. 创建一个基本的 React 应用

    Create React App 是一个用于学习 React 的舒适环境,也是用 React 创建新的单页应用的最佳方式。

    它会配置你的开发环境,以便使你能够使用最新的 JavaScript 特性,提供良好的开发体验,并为生产环境优化你的应用程序。你需要在你的机器上安装 Node >= 8.10 和 npm >= 5.6。要创建项目,请执行:

    npx create-react-app my-app
    cd my-app
    npm start
  2. 用 React 实现 to-do-list

import React, { Component } from "react";

class Todo extends Component {
  constructor(props) {
    super(props);
    this.state = {
      list: [],
      title: "",
    };
  }

  render() {
    const list = this.state.list;
    return (
      <div>
        <input
          type="text"
          value={this.state.title}
          onChange={this.changeHandle.bind(this)}
        />
        <button onClick={this.clickHandle.bind(this)}>Submit</button>
        <ul>
          {list.map((item, index) => {
            return <li key={index}> {item}</li>;
          })}
        </ul>
      </div>
    );
  }

  changeHandle(e) {
    this.setState({
      title: e.target.value,
    });
  }

  addTitle(title) {
    const currentList = this.state.list;
    this.setState({
      list: currentList.concat(title),
    });
    console.log(this.state.list);
  }

  clickHandle(e) {
    const title = this.state.title;
    this.addTitle(title);
    this.setState({
      title: "",
    });
  }
}

export default Todo;

mark

# 7-1 说一下对组件化的理解

# 知识点

  • 组件的 封装
  • 组件的 复用

# 组件的封装

  • 视图
  • 数据
  • 变化逻辑 ( 数据驱动视图变化 )

mark

# 组件的 复用

  • props 传递
  • 复用

mark

mark

# 题目

  • 说一下对组件化的理解?
    • 组件的封装:封装视图、数据、变化逻辑
    • 组件的复用:props 传递、复用

# 7-2 JSX 本质是什么

# 知识点

  • JSX 语法
  • JSX 解析成 JS
  • 独立的标准

# JSX 语法

  • html 形式
  • 引入 JS 变量和表达式
  • if … else …
  • 循环
  • style 和 className
  • 事件

# 提出疑问

  • JSX 语法根本无法被浏览器所解析
  • 那么它如何在浏览器运行

# JSX 解析

  • JSX 其实是语法糖
  • 开发环境会将 JSX 编译成 JS 代码
  • JSX 的写法 大大降低了学习成本和 编码工作量

mark

mark

# JSX 独立的标准

  • JSX 是 React 引入的,但不是 React 独有的
  • React 已经将它作为一个独立的标准开放,其它项目也可用
  • React.createElement 是可以自定义修改的
  • 说明:本身功能已经完备;和其它标准兼容和扩展性没问题

# 问题解答

  • JSX 本质是什么?
    • JSX 语法(标签、JS 表达式、判断、循环、事件绑定)
    • JSX 本质就是语法糖,需被解析成 JS 才能运行
    • JSX 是独立的标准,可被其它项目使用

# 7-3 JSX 和 vdom 的关系

# 知识点

  • 分析:为何需要 vdom
  • React.createElement 和 h
  • 何时 patch?
  • 自定义组件的解析

# 为何需要 vdom

  • vdom 是 React 初次推广开来的,结合 JSX
  • JSX 就是模板,最终要渲染成 html
  • 初次渲染 + 修改 state 后的 re-render
  • 正好 符合 vdom 的应用场景

# 回顾 vdom

  • vdom 如何应用,核心 API 是 什么?
    • 如何使用? 可用 snabbdom 的 用法 来 举例
    • 核心 函数 :h 函数,patch 函数

核心 API

  • h(’<标签名>’,{ … 属性 … },[… 子元素 …])
  • h(’<标签名>’,{ … 属性 … },[ ‘…’])
  • patch(container,vnode)
  • patch(vnode,newVnode)

# React.createElement 和 h

mark

mark

# 何时 patch

  • 初次渲染 - ReactDOM.render (<App />,container)
  • 会触发 patch (container,vnode)
  • re-render - setState
  • 会触发 patch (vnode,newVnode)

# 自定义组件的解析

  • ‘div’ - 直接渲染 <div> 即可,vdom 可以做到
  • Input 和 List ,是自定义组件 (class),vdom 默认不认识
  • 因此 Input 和 List 定义的时候 必须声明 render 函数
  • 根据 props 初始化实例,然后执行实例的 render 函数
  • render 函数返回的还是 vnode 对象

mark

# 问题解答

  • 为何需要 vdom:JSX 需要渲染成 html,数据驱动视图
  • React.createElement 和 h ,都生成 vnode
  • 何时 patch:ReactDOM.render (…) 和 setState
  • 自定义组件的解析:初始化实例,然后执行 render

# 7-4 说一下 setState 的过程

# 知识点

  • setState 的异步
  • vue 修改属性也是 异步
  • setState 的过程

# setState 的异步

mark

# setState 为何需要异步?

  • 可能会一次执行 多次 setState
  • 你无法规定、限制用户如何使用 setState
  • 没必要每次 setState 都重新渲染,考虑性能
  • 即便是每次重新渲染,用户也看不到中间的效果
  • 只看到最后的结果即可

mark

# vue 修改属性也是异步

  • 效果、原因和 setState 一样
  • 对比记忆,印象深刻

# vue 的整个实现流程

  • 第一步:解析模板成 render 函数
  • 第二步:响应式开始监听
  • 第三步:首次渲染,显示页面,且绑定依赖
  • 第四步:data 属性变化( 异步 ),触发 rerender
# data 属性变化
  • 修改属性,被响应式 的 set 监听到
  • set 中执行 updataComponent ( 异步
  • updataComponent 重新执行 vm.render ()
  • 生成的 vnode 和 prevVnode,通过 patch 进行比较
  • 渲染到 html 中

# setState 的过程

  • 每个组件实例,都有 renderComponent 方法
  • 执行 renderComponent 会重新执行实例的 render
  • render 函数返回 newVnode,然后拿到 preVnode
  • 执行 patch (preVnode,newVnode)
/* renderComponent方法 大致模拟*/
class Component {
  constructor(props) {}

  renderComponent() {
    const preVnode = this._vnode;
    const newVnode = this.render();
    patch(preVnode, newVnode);
    this._vnode = newVnode;
  }
}

# 问题解答

  • setState 的异步:效果、原因
  • vue 修改属性也是异步:效果、原因
  • setState 的过程:最终走到 patch (preVnode, newVnode)

# 7-5 总结

  • 说一下对组件化的理解?

    • 组件的封装:封装视图、数据、变化逻辑
    • 组件的复用:props 传递、复用
  • JSX 本质是什么?

    • JSX 语法(标签、JS 表达式、判断、循环、事件绑定)
    • JSX 本质就是语法糖,需被解析成 JS 才能运行
    • JSX 是独立的标准,可被其它项目使用
  • JSX 和 vdom 的关系?

    • 为何需要 vdom:JSX 需要渲染成 html,数据驱动视图
    • React.createElement 和 h ,都生成 vnode
    • 何时 patch:ReactDOM.render (…) 和 setState
    • 自定义组件的解析:初始化实例,然后执行 render
  • 说一下 setState 的过程

    • setState 的异步:效果、原因
    • vue 修改属性也是异步:效果、原因
    • setState 的过程:最终走到 patch (preVnode, newVnode)

# 7-6 React Vs vue

# 知识点

  • 两者的本质区别
  • 看模板和组件化的区别
  • 两者共同点
  • 总结问题的答案

# 两者的本质区别

  • vue - 本质是 MVVM 框架,由 MVC 发展而来
  • React - 本质是前端组件化框架,有后端组件化发展而来

# 模板的区别

  • vue - 使用模板 ( 最初由 angular 提出)
  • React - 使用 JSX
  • ** 模板语法 ** 上,我更倾向于 JSX
  • 模板分离 上,我更加倾向于 vue

mark

mark

mark

  • 模板应该 和 JS 逻辑分离
  • 回顾 ” 开放封闭原则 “

# 组件化的区别

  • React 本身就是组件化,没有组件化就不是 React
  • vue 也支持组件化,不过是在 MVVM 上的扩展
  • 查阅 vue 组件化的文档,洋洋洒洒很多( 侧面反映 )
  • 对于组件化,我更倾向于 React,做的彻底而清晰

# 两者的共同点

  • 都支持组件化
  • 都是数据驱动视图

# 问题解答

  • 阐述一下对 React 和 Vue 的 认识
    • 国内使用,首推 vue。文档更易读、易学、社区够大
    • 如果团队水平较高,推荐使用 React。组件化 和 JSX

MVVM 和 vue

# MVVM 和 vue

本章首先介绍了 jQuery 开发方式和框架开发方式的区别 ,引导学生进入框架开发的思路转变。然后通过 MVC 模式引入 MVVM ,在两者比较让学生更快熟悉 MVVM 。最后结合实例,详细讲解 vue 的 实现原理 ,包括 响应式模板解析渲染 这三大要素。…

高级面试:vue 知识点

放弃该放弃的是无奈,放弃不该放弃的是无能,不放弃该放弃的是无知,不放弃不该放弃的是执著!

It is helpless to give up the waiver. It is incompetence to give up what should not be given up. It is ignorance that does not give up the waiver. It is attachment that does not give up and should not give up!

# 知识点

  • 如何 理解 MVVM
  • 如何 实现 MVVM
  • 是否解读过 vue 源码

# 题目

  • 说一下 使用 jQuery 和 使用框架的区别
  • 说一下 对 MVVM 的理解
  • vue 中如何实现 响应式
  • vue 中如何解析 模板
  • vue 的 整个实现流程

# 使用 jQuery 和 使用框架的区别

# 实现

  • jQuery 实现 todo-list
  • vue 实现 todo-list
  • jQuery 和 框架的区别

# jQuery 实现 todo-list

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <input type="text" id="txt-title" />
    <button id="btn-submit">submit</button>
    <div>
      <ul id="ul-list"></ul>
    </div>
    <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
    <script>
      var $txtTitle = $("#txt-title");
      var $ulList = $("#ul-list");
      var $btnSubmit = $("#btn-submit");
      $btnSubmit.click(function () {
        var title = $txtTitle.val();
        if (!title) {
          return;
        }
        var $li = $(`<li>${title}</li>`);
        $ulList.append($li);
        $txtTitle.val("");
      });
    </script>
  </body>
</html>

mark

# vue 实现 todo-list

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="app">
      <div>
        <input type="text" v-model="title" />
        <button @click="add">submit</button>
      </div>
      <ul>
        <li v-for="item in list">{{item}}</li>
      </ul>
    </div>

    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          title: "",
          list: [],
        },
        methods: {
          add: function () {
            this.list.push(this.title);
            this.title = "";
          },
        },
      });
    </script>
  </body>
</html>

mark

# 两者的区别

  • 数据视图 的分离
  • 以数据驱动视图

# 问题解答

  • 说一下 使用 jQuery 和 使用框架的区别 ?
    • 数据视图 的分离,解耦开放封闭原则
    • 以数据驱动视图,只关心数据变化,DOM 操作被封装

# 说一下 对 MVVM 的理解

目前前端框架中,最为出色的要属 Vue 和 React 了,这俩个框架的核心理念都是数据驱动页面渲染,同时他们都是 MVVM 模式的框架,MVVM 模式 中的 M 还是固定表示 Modal,V 还是表死 View,这俩个基本都是不会发生变化,一个页面必然需要数据和渲染俩个部分,那么变化的是如何将 Modal 渲染到 View 的过程变了,在 MVVM 模式中,将 View 和 Modal 绑定在一起,只要 Modal 发生了变化,View 就会自动更新,不需要我们认为的再去写如何操作 DOM 更新的过程了

# 知识点

  • MVC
  • MVVM
  • 关于 ViewModel

# MVC

  • M -Model 数据
  • V - View 视图、界面
  • C - Controller 控制器 、逻辑处理

mark

MVC 模式(Model–view–controller)是软件工程中的一种软件架构模式,把软件系统分为三个基本部分:模型(Model)、视图(View)和控制器(Controller)。

在前端的 MVC 模式 中,M 还是表示 Modal 层,负责与后台交互数据,V 表示 View,负责页面上 DOM 的渲染,C 表示绑定在 DOM 元素上的事件,当 Controllor 中的事件被调用,会去调用 Modal 中的数据,然后交给 View 重新渲染数据

# MVVM

  • M - Model 模型、数据
  • V - View 视图、模板( 视图和模板是分离的 )
  • ViewModel - 连接 Model 和 View

mark

mark

# 关于 ViewModel

  • MVVM 不算是一种创新
  • 但其中的 ViewModel 确实是一种创新
  • 真正 结合前端场景应用的创建

# 问题解答

  • 说一下 对 MVVM 的理解
    • MVVM - Model View ViewModel
    • 三者之间的联系。以及如何对应到各段代码
    • ViewModel 的理解,联系 View 和 Model

# MVVM 框架的三大要素

# 流程

  • 再次分析 demo
  • 三要素总结

# 三要素

  • 响应式:vue 如何监听到 data 的每个属性变化?
  • 模板引擎:vue 的模板如何被解析,指令如何处理?
  • 渲染:vue 的模板如何被渲染成 html?以及渲染过程

# vue 中如何实现响应式

# 知识点

  • 什么是响应式?
  • Object.defineProperty
  • 模拟

# 什么是响应式?

  • 修改 data 属性后,vue 立刻监听到
  • data 属性被代理到 vm 上
  • 演示
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <div id="app">
      <div>{{age}}</div>
      <div>{{name}}</div>
    </div>
    <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
    <script>
      var vm = new Vue({
        el: "#app",
        data: {
          name: "张三",
          age: "18",
        },
      });
    </script>
  </body>
</html>

mark

# Object.defineProperty

Object.defineProperty() 方法会直接在一个对象上定义一个新属性,或者修改一个对象的现有属性, 并返回这个对象。

语法 :Object.defineProperty (obj, prop, descriptor)

参数

Object.defineProperty(obj, prop, descriptor)

obj :要在其上定义属性的对象。

prop :要定义或修改的属性的名称。

descriptor :将被定义或修改的属性描述符。

var obj = {
  name: "张三",
  age: 25,
};
console.log(obj.name); //获取属性的时候,如何监听到?
obj.age = 26; //赋值属性的时候,如何监听到?

Object.defineProperty:

var obj = {};
var name = "张三";
Object.defineProperty(obj, "name", {
  get: function () {
    console.log("get");
    return name;
  },
  set: function (newVal) {
    console.log("set");
    name = newVal;
  },
});

console.log(obj.name);
obj.name = "list";

# 模拟实现

模拟实现 Vue 如何监听 data

/*
* 如何监听data
var vm=new Vue({
    el:'张三',
    data:{
        name:'张三',
        age:20
    }
})
*/

//模拟实现
var vm = {};
var data = {
  price: 100,
  name: "张三",
};

var key, value;
for (key in data) {
  //命中闭包。新建一个函数,保证 key 的独立的作用域
  (function (key) {
    Object.defineProperty(vm, key, {
      get: function () {
        console.log("get"); //监听
        return data[key];
      },
      set: function (newVal) {
        console.log("set"); //监听
        data[key] = newVal;
      },
    });
  })(key);
}

mark

# 问题解答

  • 什么是响应式?
    • 关键是理解 Object.defineProperty
    • 将 data 的属性代理到 vm 上

# vue 中如何解析模板

# 知识点

  • 模板是什么?
  • render 函数
  • render 函数 与 vdom

# 模板是什么?

  • 本质:字符串

  • 有逻辑,如 v-if 、v-for 等

  • 与 html 格式很像,但有很大区别

  • 最终还要转换为 html 来显示

  • 模板最终必须转化成 JS 代码 ,因为:

    • 有逻辑(v-if、v-for),必须用 JS 才能实现 ( 图灵完备 )
    • 转换成 html 渲染页面,必须用 JS 才能实现
    • 因此,模板最重要转换成 一个 JS 函数 (render 函数)
<div id="app">
  <div>
    <input type="text" v-model="title" />
    <button @click="add">submit</button>
  </div>
  <ul>
    <li v-for="item in list">{{item}}</li>
  </ul>
</div>

# render 函数 - with 的用法

with 的用法 — 自己开发的代码尽量不要使用!

var obj = {
  name: "张三",
  age: 20,
  getAddress: function () {
    alert("beijing");
  },
};

// 不使用 with
function fn() {
  alert(obj.name);
  alert(obj.age);
  obj.getAddress();
}
fn();
var obj = {
  name: "张三",
  age: 20,
  getAddress: function () {
    alert("beijing");
  },
};

// 使用 with
function fn1() {
  with (obj) {
    alert(obj.name);
    alert(obj.age);
    getAddress();
  }
}
fn1();

# render 函数

mark

# 总结

  • 模板中所有信息都包含在了 render 函数中
  • this 即 vm
  • price 即 this.price 即 vm.price ,即 data 中的 price
  • _c 即 this. _c 即 vm. _c

# render 函数剖析

  • 从哪里可以看到 render 函数?
  • 复杂一点的例子,render 函数是什么样子的?
  • v- if 、v-for 、v-on 都是怎么处理的?

# 看一下 todo-list demo 的 render 函数

<div id="app">
  <div>
    <input type="text" v-model="title" />
    <button @click="add">submit</button>
  </div>
  <ul>
    <li v-for="item in list">{{item}}</li>
  </ul>
</div>
with (this) {
  return _c("div", { attrs: { id: "app" } }, [
    _c("div", [
      _c("input", {
        directives: [
          {
            name: "model",
            rawName: "v-model",
            value: title,
            expression: "title",
          },
        ],
        attrs: { type: "text" },
        domProps: { value: title },
        on: {
          input: function ($event) {
            if ($event.target.composing) return;
            title = $event.target.value;
          },
        },
      }),
      _v(" "),
      _c("button", { on: { click: add } }, [_v("submit")]),
    ]),
    _v(" "),
    _c(
      "ul",
      _l(list, function (item) {
        return _c("li", [_v(_s(item))]);
      }),
      0
    ),
  ]);
}
  • 根据 todo-list demo 的 render 函数
  • v- model 是怎么实现的?
  • v-on :click 是怎么实现的?
  • v- for 是怎么实现的?

# 重要

/*
    vue2.0 开始支持预编译

    开发环境 :写模板

    编译打包

    生产环境:JS
    --------------------------------------------
    React 组件化
    
    JSX 模板
    编译 : -> JS 代码
    
*/

# 渲染

  • 已经解决了 模板中 “逻辑” (v-for、v-if )的问题

  • 还剩下模板生成 html 问题

  • 另外,vm._c 是什么? render 函数 返回了什么?

# 先复习一下 vdom 的知识

可参考 vdom 篇

vdom 的如何应用,核心 API 是什么?

  • 介绍 snabbdom
  • 重做 之前的 demo
  • 核心 API
# render 函数和 vdom

mark

mark

  • updataComponent 中实现了 vdom 的 patch
  • 页面首次渲染执行 updataComponent
  • data 中每次修改属性,执行 updataComponent

# 问题解答

  • vue 中如何解析 模板
    • 模板:字符串,有逻辑,嵌入 JS 变量…
    • 模板必须转换成 JS 代码(有逻辑,渲染 html,JS 变量)
    • render 函数是什么样子的
    • render 函数执行是返回 vnode
    • updataComponent

# vue 的整个实现流程

# 整个实现流程

  • 第一步:解析模板成 render 函数

    • with 的用法
    • 模板中的所有信息都被 render 函数 包含
    • 模板中用到的 data 中的属性,都变成了 JS 变量
    • 模板中的 v-model、v-for、v-on 都变成了 JS 逻辑
    • render 函数返回 vnode
  • 第二步:响应式开始监听

    • Object.defineProperty
    • 将 data 的属性代理到 vm 上
  • 第三步:首次渲染,显示页面,且绑定依赖

    • 初次渲染,执行 updataComponent ,执行 vm.render ()
    • 执行 render 函数,会访问到 vm.list 和 vm.title
    • 会被响应式的 get 方法监听到 (后面详细讲)
    • 执行 updataComponent ,会被 vdom 的 patch 方法
    • patch 将 vnode 渲染成 DOM ,初次渲染完成

    • 为何要监听 get,直接监听 set 不行吗?
    • data 中有很多属性,有些被用到,有些可能不被用到
    • 被用到的会走到 get ,不被用到的 不会走到 get
    • 未走到 get 中的属性,set 的时候我们也无需关心
    • 避免不必要的重复渲染
  • 第四步:data 属性变化,触发 rerender

    • 修改属性,被响应式 的 set 监听到
    • set 中执行 updataComponent
    • updataComponent 重新执行 vm.render ()
    • 生成的 vnode 和 prevVnode,通过 patch 进行比较

# 问题解答

  • vue 的整个实现流程?
    • 第一步:解析模板成 render 函数
    • 第二步:响应式开始监听
    • 第三步:首次渲染,显示页面,且绑定依赖
    • 第四步:data 属性变化,触发 rerender

# 总结

  • 说一下 使用 jQuery 和 使用框架的区别

    • 数据视图 的分离,解耦开放封闭原则
    • 以数据驱动视图,只关心数据变化,DOM 操作被封装
  • 什么是 MVVM ?

    • MVVM - Model View ViewModel

    • 三者之间的联系。以及如何对应到各段代码

    • ViewModel 的理解,联系 View 和 Model

# 三要素

  • 响应式:vue 如何监听到 data 的每个属性变化?

  • 模板引擎:vue 的模板如何被解析,指令如何处理?

  • 渲染:vue 的模板如何被渲染成 html?以及渲染过程?

  • Vue 如何实现响应式?

    • 关键是理解 Object.defineProperty
    • 将 data 的属性代理到 vm 上
  • vue 中如何解析 模板

    • 模板:字符串,有逻辑,嵌入 JS 变量…
    • 模板必须转换成 JS 代码(有逻辑,渲染 html,JS 变量)
    • render 函数是什么样子的
    • render 函数执行是返回 vnode
    • updataComponent
  • vue 的整个实现流程?

    • 第一步:解析模板成 render 函数
    • 第二步:响应式开始监听
    • 第三步:首次渲染,显示页面,且绑定依赖
    • 第四步:data 属性变化,触发 rerender

微信小程序笔记上

# 微信小程序笔记上

微信小程序学习笔记上

初步接触小程序

基础内容介绍

开发者文档

app.js 全局生命周期函数

WXML 数据绑定

通过 wx.request 请求数据

微信小程序里列表渲染

页面相关事件处理函数

True mastery of any skill takes a lifetime.

对任何技能的掌握都需要一生的刻苦操练。

# 小程序简介

小程序是一种全新的连接用户与服务的方式,它可以在微信内被便捷地获取和传播,同时具有出色的使用体验。

微信小程序使用了前端技术栈 JavaScript/WXML/WXSS。

相关介绍

微信小程序使用了前端技术栈 JavaScript/WXML/WXSS。但和常规的前端开发又有一些区别: 可以参考

  1. JavaScript: 微信小程序的 JavaScript 运行环境即不是 Browser 也不是 Node.js。它运行在微信 App 的上下文中,不能操作 Browser context 下的 DOM,也不能通过 N*ode.js 相关接口访问操作系统 API。所以,严格意义来讲,微信小程序并不是 Html5,虽然开发过程和用到的技术栈和 Html5 是相通的。
  2. **WXML:** 作为微信小程序的展示层,并不是使用 Html,而是自己发明的基于 XML 语法的描述。
  3. **WXSS:** 用来修饰展示层的样式。官方的描述是 “WXSS (WeiXin Style Sheets) 是一套样式语言,用于描述 WXML 的组件样式。WXSS 用来决定 WXML 的组件应该怎么显示。” “我们的 WXSS 具有 CSS 大部分特性… 我们对 CSS 进行了扩充以及修改。” 基于 CSS2 还是 CSS3?大部分是哪些部分?是否支持 CSS3 里的动画?不得而知。

# 目录结构:

mark

  1. app.js 做为小程序的入口,里面有个 App 实例,每个小程序只会有一个 App 实例,小程序启动以后触发 onLaunch 函数执行,获取用户信息
  2. app.json 是小程序的所有全局配置, pages 字段放置所有页面的路径, window 字段定义所有页面的顶部背景颜色,文字颜色 详细配置请戳这里 👇
  3. app.wxss 文件就是页面公用的样式,仅支持部分 css 选择器
  4. wxml 就是我们的 HTML 文件,常用标签为 viewtext 等,没有所谓的 divspanp 一类的标签了,我们习惯称它们为组件

设计理念:

小程序内部可以理解成一个 mvvm 的框架,分为逻辑层和视图层,逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。


mark

mark

# 开始上手

开始正式学习

基础学习部分

# 加载一张图片

mark

简单使用样式

mark

# WXSS

WXSS (WeiXin Style Sheets) 是一套样式语言,用于描述 WXML 的组件样式。

WXSS 用来决定 WXML 的组件应该怎么显示。 可以参考官网

为了适应广大的前端开发者,WXSS 具有 CSS 大部分特性。同时为了更适合开发微信小程序,WXSS 对 CSS 进行了扩充以及修改。

与 CSS 相比,WXSS 扩展的特性有:

  • 尺寸单位
  • 样式导入

# 尺寸单位

rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为 750rpx。如在 iPhone6 上,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素

mark

建议: 开发微信小程序时设计师可以用 iPhone6 作为视觉稿的标准。

注意: 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。

# 全局样式与局部样式

定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。

# 开发者文档

开发者文档

不会忘了 就查文档!

# image:

解决图片缩放问题:

  • mode ( 常用下面 2 个属性值缩放图片 )
    • aspectFit:缩放模式,保持纵横比缩放图片,使图片的长边能完全显示出来。也就是说,可以完整地将图片显示出来。
    • aspectFill:缩放模式,保持纵横比缩放图片,只保证图片的短边能完全显示出来。也就是说,图片通常只在水平或垂直方向是完整的,另一个方向将会发生截取。

mark

# swiper

看开发者文档

轮播组件:调用相应的标签及参数设置

mark

# 解读 app.json

小程序根目录下的 app.json 文件用来对微信小程序进行全局配置。文件内容为一个 JSON 对象,有以下属性:

可以参考官网

# pages

用于指定小程序由哪些页面组成,每一项都对应一个页面的 路径(含文件名) 信息。文件名不需要写文件后缀,框架会自动去寻找对于位置的 .json , .js , .wxml , .wxss 四个文件进行处理。

# window

用于设置小程序的状态栏、导航条、标题、窗口背景色。

例如:

{
  "window": {
    "navigationBarBackgroundColor": "#ffffff",
    "navigationBarTextStyle": "black",
    "navigationBarTitleText": "微信接口功能演示",
    "backgroundColor": "#eeeeee",
    "backgroundTextStyle": "light"
  }
}

# tabBar

如果小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

"tabBar": {
   "color": "#333",
   "selectedColor": "#f30",
   "list": [{
     "pagePath": "pages/index/index",
     "text": "首页",
     "iconPath": "assets/tabs/icon1.png",
     "selectedIconPath": "assets/tabs/icon1.png"
   },
   {
     "pagePath": "pages/message/message",
     "text": "消息",
     "iconPath": "assets/tabs/icon1.png",
     "selectedIconPath": "assets/tabs/icon1.png"
   },
   {
     "pagePath": "pages/index/index",
     "text": "个人中心",
     "iconPath": "assets/tabs/icon1.png",
     "selectedIconPath": "assets/tabs/icon1.png"
   },
   {
     "pagePath": "pages/index/index",
     "text": "综合",
     "iconPath": "assets/tabs/icon1.png",
     "selectedIconPath": "assets/tabs/icon1.png"
   }]
 }

mark

等等等… 不了解就看文档!

注意: 所以看开发者文档很重要!

点一下就可以看了,别懒!

# app.js 全局生命周期函数

app.js 全局生命周期函数

在 app.js 文件中 , 定义了一些生命周期方法 , onLaunch,onShow,onHide,onError,以及任意开发者添加的函数或者数据(通过 this 可以访问)

以下是各个生命周期方法作用和描述:

  1. onLaunch 生命周期函数 -- 监听小程序初始化 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
  2. onShow 生命周期函数–监听小程序显示 当小程序启动,或从后台进入前台显示,会触发 onShow
  3. onHide 生命周期函数–监听小程序隐藏 当小程序从前台进入后台,会触发 onHide
  4. onError 错误监听函数 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息

其他 Any 开发者可以添加任意的函数或数据到 Object 参数中,用 this 可以访问

index.js: (个人感觉 和 Vue 真的好像,如果熟悉 Vue 的话,这理解起来应该没有难度。)

//index.js
//获取应用实例
const app = getApp();

Page({
  data: {
    motto: "Hello World",
    listarr: 0,
  },
  //监听小程序初始化 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
  onLoad: function (option) {
    wx.request({
      url: "https://www.baifubao.com/callback",
      success: (res) => {
        this.setData({
          listarr: res.statusCode,
        });
      },
    });
  },
});

注意:着重关注 onLoad 函数,因为使用最频繁!

# WXML 数据绑定

WXML 中的动态数据均来自对应 Page 的 data。

# 内容

<view>{{ message }}</view>
Page({
  data: {
    message: "Hello World!",
  },
});

mark

# 特别注意:

  1. 花括号和引号之间不能有空格
  2. 不能直接写 checked=“false”,其计算结果是一个字符串,转成 boolean 类型后转为真值。

# 通过 wx.request 请求数据

通过 wx.request 请求数据

wx.request({
  url: "",
  data: "",
  header: {},
  method: "GET",
  dataType: "json",
  responseType: "text",
  success: function (res) {
    console.log(res);
  },
  fail: function (res) {},
  complete: function (res) {},
});

mark

setData:接受 wx.request 请求的数据,赋值给 Page 里面的 data(然后通过插值表达式渲染到页面)

//index.js
//获取应用实例
const app = getApp();

Page({
  data: {
    motto: "Hello World",
    listarr: 0,
  },
  onLoad: function (option) {
    wx.request({
      url: "https://www.baifubao.com/callback",
      success: (res) => {
        console.log(res);

        this.setData({
          listarr: res.statusCode,
        });
      },
    });
  },
});

# 微信小程序里列表渲染

# wx:for

在组件上使用 wx:for 控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。

默认数组的当前项的下标变量名默认为 index ,数组当前项的变量名默认为 item

类比于 Vue:

<ul id="example-1">
  <li v-for="item in items">{{ item.message }}</li>
</ul>

微信小程序里面:

<view wx:for="{{array}}">{{index}}: {{item.message}}</view>
Page({
  data: {
    array: [
      {
        message: "foo",
      },
      {
        message: "bar",
      },
    ],
  },
});

# wx:key

可以参考

如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 ](https://developers.weixin.qq.com/miniprogram/dev/component/input.html) 中的输入内容,[ 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。

  1. 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
  2. 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:

当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。

如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

<switch wx:for="{{objectArray}}" wx:key="unique" style="display: block;">
  {{item.id}}
</switch>
<button bindtap="switch">Switch</button>
<button bindtap="addToFront">Add to the front</button>

<switch wx:for="{{numberArray}}" wx:key="*this" style="display: block;">
  {{item}}
</switch>
<button bindtap="addNumberToFront">Add to the front</button>
Page({
  data: {
    objectArray: [
      { id: 5, unique: "unique_5" },
      { id: 4, unique: "unique_4" },
      { id: 3, unique: "unique_3" },
      { id: 2, unique: "unique_2" },
      { id: 1, unique: "unique_1" },
      { id: 0, unique: "unique_0" },
    ],
    numberArray: [1, 2, 3, 4],
  },
});

# 页面相关事件处理函数

页面相关事件处理函数(下拉刷新滑到底部加载更多等)

可以参考

下拉事件和触底事件

//页面相关事件处理函数---监听用户下拉动作
   onPullDownRefresh:function(){
     console.log('下拉!')
   },
   //页面上拉触底事件的处理函数
   onReachBottom:function(){
     console.log('上拉')
   }

# 导航栏

可以参考官方文档

js 文件

函数 API 功能
wx.showNavigationBarLoading(Object object) 在当前页面显示导航条加载动画
wx.setNavigationBarTitle(Object object) 动态设置当前页面的标题
wx.setNavigationBarColor(Object object) 设置页面导航条颜色
wx.hideNavigationBarLoading(Object object) 在当前页面隐藏导航条加载动画

mark

# 界面交互

参考官方文档

# 示例代码

wx.showToast({
  title: "成功w",
  icon: "success",
  duration: 2000,
});

mark

# 下拉刷新

下拉刷新 ( 配合 页面相关事件处理函数 — 监听用户下拉动作)

index.json:

{
  "enablePullDownRefresh": true,
  "backgroundTextStyle": "dark"
}

index.js:

//页面相关事件处理函数---监听用户下拉动作
 onPullDownRefresh:function(){
   console.log('下拉!')
 },

mark

微信小程序笔记下

# 微信小程序笔记下

微信小程序笔记下

input

color 属性

bindchange

wxs 页面脚本

微信小程序事件 冒泡 ** 和 事件传参 **

服务号、订阅号

sdk

# 微信小程序组件

# 表单组件

# input

输入框。该组件是原生组件,使用时请注意相关限制

可以参考

<!-- 输入框 -->
<view>
  <input type='text' placeholder='text'></input>
  <input type='password' placeholder='password'></input>
  <input type='number' placeholder='number'></input>
  <input type='idcard' placeholder='idcard'></input>
  <input type='digit' placeholder='digit'></input>

  <!-- 单选框 -->
  <radio-group>
     <radio>单选框</radio>
     <radio>单选框</radio>
  </radio-group>

  <!-- 多选框 -->
<checkbox-group>
   <checkbox>多选框</checkbox>
   <checkbox>多选框</checkbox>
   <checkbox>多选框</checkbox>
</checkbox-group>

</view>

mark

# 改变颜色

color 属性

<!-- 单选框 -->
<radio-group>
  <radio color="#f30">单选框</radio>
  <radio>单选框</radio>
</radio-group>

<!-- 多选框 -->
<checkbox-group>
  <checkbox color="#f30">多选框</checkbox>
  <checkbox>多选框</checkbox>
  <checkbox>多选框</checkbox>
</checkbox-group>

mark

  • checked:默认选中
  • disabled: 禁用

# bindchange

checkbox-group 中选中项发生改变时触发 change 事件,detail =

单选框复选框 都能用!

例如:

<!-- 单选框 -->
<radio-group ="radiobindchangefun" data-index="1">
  <radio color="#f30" value="nan">单选框</radio>
  <radio value="nv">单选框</radio>
</radio-group>

index.js:

radiobindchangefun(e){
  console.log(e.detail.value)
}

mark

# wxs 页面脚本

WXS(WeiXin Script)是小程序的一套脚本语言,结合 WXML ,可以构建出页面的结构。

WXS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。

用的相对不多

<wxs module="foo">
  var sum=function(a,b){ return a+b; } module.exports.sum=sum
</wxs>

<view>{{foo.sum(1,2)}}</view>

mark

# 微信小程序事件冒泡和事件传参

微信小程序事件冒泡和事件传参

参考

# 事件绑定和冒泡

事件绑定的写法同组件的属性,以 key、value 的形式。

  • key 以 bindcatch 开头,然后跟上事件的类型,如 bindtapcatchtouchstart 。自基础库版本 1.5.0 起,在非原生组件中, bindcatch 后可以紧跟一个冒号,其含义不变,如 bind:tapcatch:touchstart
  • value 是一个字符串,需要在对应的 Page 中定义同名的函数。不然当触发事件的时候会报错。

bind 事件绑定不会阻止冒泡事件向上冒泡, catch 事件绑定可以阻止冒泡事件向上冒泡。

<view class="father" bindtap="ClickFatherEvent">
  <view class="son" catchtap="ClickSonEvent"> </view>
</view>

mark

# 事件传参

自定义属性传参 ( data-index=“1” )

<view class="father" bindtap="ClickFatherEvent" data-index="1">
  <view class="son" catchtap="ClickSonEvent"> </view>
</view>
ClickFatherEvent(event){
    console.log('father')
    console.log(event.currentTarget.dataset.index)
}

mark

# 服务号

服务号:为企业和组织提供更强大的业务服务与用户管理能力,主要偏向服务类交互(功能类似 12315,114,银行,提供绑定信息,服务交互的);

适用人群:媒体、企业、政府或其他组织。

群发次数:服务号 1 个月(按自然月)内可发送 4 条群发消息。

可以参考

# 订阅号

# 什么是订阅号?

订阅号:为媒体和个人提供一种新的信息传播方式,主要功能是在微信侧给用户传达资讯;(功能类似报纸杂志,提供新闻信息或娱乐趣事)

适用人群:个人、媒体、企业、政府或其他组织。

群发次数:订阅号(认证用户、非认证用户)1 天内可群发 1 条消息。

温馨提示:

  1. 如果想用公众平台简单发发消息,做宣传推广服务,建议可选择订阅号;
  2. 如果想用公众平台进行商品销售,建议可选择服务号,后续可认证再申请微信支付商户;

# sdk

软件开发工具包(缩写:SDK、外语全称:**Software Development Kit)** 一般都是一些软件工程师为特定的软件包软件框架硬件平台、操作系统等建立应用软件时的开发工具的集合。 [1]

软件开发工具包括广义上指辅助开发某一类软件的相关文档、范例和工具的集合

软件开发工具包是一些被软件工程师用于为特定的软件包、软件框架、硬件平台、操作系统等创建应用软件的开发工具的集合,一般而言 SDK 即开发 Windows 平台下的应用程序所使用的 SDK。它可以简单的为某个程序设计语言提供应用程序接口 API 的一些文件,但也可能包括能与某种嵌入式系统通讯的复杂的硬件。一般的工具包括用于调试和其他用途的实用工具。SDK 还经常包括示例代码、支持性的技术注解或者其他的为基本参考资料澄清疑点的支持文档。

为了鼓励开发者使用其系统或者语言,许多 SDK 是免费提供的。软件工程师通常从目标系统开发者那里获得软件开发包,也可以直接从互联网下载,有时也被作为营销手段。例如,营销公司会免费提供构建 SDK 以鼓励人们使用它,从而会吸引更多人由于能免费为其编程而购买其构件。

SDK 可能附带了使其不能在不兼容的许可证下开发软件的许可证。例如产品供应商提供一个专有的 SDK 可能与自由软件开发抵触。GPL 能使 SDK 与专有软件开发近乎不兼容。LGPL 下的 SDK 则没有这个问题。

可参考官方解释

微信 JS-SDK 说明文档

hybrid

# hybrid

本章主要介绍 hybrid 的原理和应用。hybrid 基础部分要讲解 file 协议、webview、更新上线流程;另外,通过 h5 和 hybrid 的对比,来了解两者的异同和使用场景;最后讲解前端 JS 和客户端的通讯,包括通讯原理和 JS-bridge 的代码封装。…

社会主流 大前端 的概念。

知识点

6-1 hybrid 是什么? 为何用 hybrid ?

6-2 hybrid 更新上线流程

6-3 hybrid 和 h5 的 主要区别?

6-4 前端 JS 和 客户端 如何通讯?

The greatest test of courage on earth is to bear defeat without losing heart.

世界上对勇气的最大考验是忍受失败而不丧失信心。

# hybrid

  • 移动端占大部分流量,已经远远超过 PC
  • 一线互联网公司都有自己的 App
  • 这些 App 中有很大比例的前端代码 (不要惊讶)
  • 拿微信举例,你每天浏览微信的内容,多少是前端?

# 知识点

  • hybrid 是什么? 为何用 hybrid ?
  • 介绍一下 hybrid 更新 和上线的流程?
  • hybrid 和 h5 的 主要区别?
  • 前端 JS 和 客户端 如何通讯?

# 6-1 hybrid 是什么? 为何用 hybrid ?

# 知识点

  • hybrid 文字解释
  • 存在价值,为何用 hybrid ?
  • webview
  • file:// 协议
  • hybrid 实现流程

# hybrid 文字解释

  • hybrid 即 “混合” ,即 前端 和客户端的混合开发
  • 需前端开发人员 和 客户端开发人员配合完成
  • 某些环节也可能涉及到 server 端
  • 注意:不要以为自己是前端就可以不理会客户端的知识

mark

# hybrid 存在价值

  • 可以快速迭代更新【关键】(无需 app 审核,思考为何?–> 前端部分能够快速更新上线)
  • 体验流畅 ( 和 NA 的体验基本类似 )
  • 减少开发和沟通成本,双端公用一套代码

# webview

  • 是 App 中的一个组件(app 可以有 webview,也可以没有)
  • 用于加载 h5 页面,即一个小型的浏览器 内核

mark

# file 协议

  • 其实在一开始接触 html 开发,就已经使用了 file 协议
  • 只不过你当时没有 “协议” “标准” 等这些概念
  • 再次强调 “协议” “标准” 的重要性 !!!

mark

mark

两者的区别:

  • file 协议:本地文件,快
  • http (s) 协议:网络加载,慢

# hybrid 具体实现

  • 不是所有的场景都适合使用 hybrid
  • 使用 NA :体验要求极致,变化不频繁( 如头条的首页 )
  • 使用 hybrid :体验要求高,变化频繁( 如头条的新闻详情页 )
  • 使用 h5 :体验无要求,不常用(如举报、反馈等页面)

具体实现

  • 前端 做好静态页面(html css js),将文件交给客户端
  • 客户端拿到前端静态页面,以文件形式储存在 app 中
  • 客户端 在一个 webview 中
  • 使用 file 协议 加载静态页面

mark

# 具体实现 – 遗留问题:

  • app 发布之后,静态文件如何实时更新?
  • 静态页面如何获取内容?

# 问题解答

  • hybrid 是什么? 为何用 hybrid ?
    • hybrid 即 “混合” ,即 前端 和客户端的混合开发
    • hybrid 存在的核心意义在于快速迭代,无需审核
    • hybrid 实现流程(图),以及 webview 和 file 协议

# 6-2 hybrid 更新上线流程

  • 回顾 hybrid 实现流程
  • 思考 (目的,实现途径)
  • 更新流程

# 回顾 hybrid 实现流程

mark

# 思考 (目的,实现途径)

mark

  • 要替换每个客户端的静态文件
  • 只能客户端来做(客户端是我们开发的)
  • 客户端去 server 下载最新的静态文件
  • 我们维护 server 的静态文件

mark

完整流程:

  • 分版本,有版本号,如 201803211015
  • 将静态 文件压缩成 zip 包,上传到服务器
  • 客户端每次启动,都去服务端检查版本号
  • 如果服务端本版本号大于客户端版本号,就去下载最新的 zip 包
  • 下载完之后解压包,然后将现有的文件覆盖

# 问题解答

  • 介绍一下 hybrid 更新 和上线的流程?
    • 掌握流程图
    • 要点 1:服务端的版本和 zip 包维护
    • 要点 2:更新 zip 包之前,先对比版本号
    • 要点 3:zip 下载解压和覆盖

# 6-3hybrid 和 h5 的 主要区别?

# 知识点

  • 优点
  • 缺点
  • 适用的场景

# hybrid 优点

  • 体验更好,跟 NA 体验基本一致
  • 可快速迭代,无需 app 审核 【关键】

# hybrid 缺点

  • 开发成本高。联调,测试,查 bug 都比较麻烦
  • 运维成本高。参考此前讲过的更新上线的流程

# 适用场景

  • hybrid :产品的稳定功能,体验要求高,迭代频繁
  • h5:单次的运营活动(如 xx 红包)或 不常用功能(反馈、举报页面等)

# 问题解答

  • hybrid 和 h5 的 主要区别?
    • 优点:体验好,可快速迭代
    • 缺点:开发成本高,运维成本高
    • 适用的场景:hybrid 适合产品型,h5 适合 运行型

# 6-4 前端 JS 和 客户端 如何通讯?

# 知识点

  • 回顾 之前遗留的问题
  • JS 和 客户端通讯的基本形式
  • schema 协议简介和使用
  • schema 使用的封装
  • 内置上线

# 之前遗留的问题

  • 新闻详情页适用于 hybrid,前端如何 获取新闻内容 ?
    • 不能用 ajax (http (s) 协议)获取。第一: 跨域 ,第二 :速度慢
    • 客户端获取新闻内容,然后 JS 通讯拿到内容,再渲染

mark

JS 和 客户端通讯的基本形式

  • JS 访问客户端能力,传递参数 和 回调函数
  • 客户端 通过 回调函数 返回内容

# schema 协议简介和使用

  • 之前介绍了 http (s) 和 file 协议
  • schema 协议 —— 前端和客户端通讯的约定

mark

mark

mark

# 模拟扫一扫前后端通讯

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <button id="btn">扫一扫</button>
    <script>
      function invokeScan() {
        window["_invoke_scan_callback_"] = function (result) {
          alert(result);
        };
        var iframe = document.createElement("iframe");
        iframe.style.display = "none";
        // iframe.src = 'weixin://dl/scan'  //重要
        iframe.src =
          "weixin://dl/scan?k1=v1&k2=v2&k3=v3&callback=_invoke_scan_callback_";
        var body = document.body;
        body.appendChild(iframe);
        setTimeout(function () {
          body.removeChild(iframe);
          iframe = null;
        });
      }

      document.getElementById("btn").addEventListener("click", function () {
        invokeScan();
      });
    </script>
  </body>
</html>

# schema 使用的封装

mark

mark

mark

# schema 封装代码

schema 使用的封装

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
  </head>
  <body>
    <button id="btn1">扫一扫</button>
    <button id="btn2">分享</button>
    <script src="invoke.js"></script>
    <script>
      //调用扫一扫
      document.getElementById("btn1").addEventListener("click", function () {
        window.invoke.scan({}, function () {});
      });

      //调用分享
      document.getElementById("btn2").addEventListener("click", function () {
        window.invoke.share(
          {
            title: "xxx",
            content: "yyy",
          },
          function (relust) {
            if (relust.error === 0) {
              alert("分享成功");
            } else {
              alert(relust.message);
            }
          }
        );
      });
    </script>
  </body>
</html>

invoke.js

// invoke.js
(function (window, undefined) {
  //调用 schema 的封装
  function _invoke(action, data, callback) {
    //拼装 schema 协议
    var schema = "myapp://utils/" + action;

    //拼接参数
    schema += "?a=a";
    var key;
    for (key in data) {
      if (data.hasOwnProperty(key)) {
        schema += "&" + key + data[key];
      }
    }

    //处理 callback
    var callbackName = "";
    if (typeof callback === "string") {
      callbackName = callback;
    } else {
      callbackName = action + Date.now();
      window[callbackName] = callback;
    }
    schema += "callback=callbackName";

    //触发
    var iframe = document.createElement("iframe");
    iframe.style.display = "none";
    iframe.src = schema; //重要
    var body = document.body;
    body.appendChild(iframe);
    setTimeout(function () {
      body.removeChild(iframe);
      iframe = null;
    });
  }

  //暴露到全局变量
  window.invoke = {
    share: function (data, callback) {
      _invoke("share", data, callback);
    },
    scan: function (data, callback) {
      _invoke("scan", data, callback);
    },
    login: function (data, callback) {
      _invoke("login", data, callback);
    },
  };
})(window);

# 问题解答

  • 前端 JS 和 客户端 如何通讯?
    • 通讯的基本形式:调用能力,传递参数,监听回调
    • 对 schema 协议的理解和使用 (这个协议可理解为 :定义了前端和客户端 通信的一个标准)
    • 调用 schema 代码的封装
    • 内置上线的好处:更快、更安全

异步

# 异步

本章全面讲解了 JS 异步的知识点。先从原理开始 ,讲解什么是 单线程、什么是 event loop ;然后讲解 jQuery 中解决 异步的 Deferred 以及 jQuery 初次展示出来的 Promise 的用法;最后再讲解 ES6 中 Promise 的用法和标准

基础篇请看异步和单线程

# 异步

高级面试

  • 前端 JS 面试技巧》讲到异步的基础
  • 高级面试会问到更多的内容
  • 如 event-loop Promise Async/Await 等

# 问题

  • 什么是单线程,和异步有什么关系? 参考
  • 什么是 event-loop?
  • 是否用过 jQuery 的 Deferred
  • Promise 的基本使用和原理
  • 介绍一下 async/await(ES7)(和 Promise 的区别、联系)
  • 总结一下当前 JS 解决异步的方案

# 4-1 单线程和异步

单线程和异步基础篇

  • 为什么是单线程?
    • 原因 - 避免 DOM 渲染冲突
      • 浏览器需要渲染 DOM
      • JS 可以修改 DOM 结构
      • JS 执行的时候,浏览器 DOM 渲染会暂停
      • 两段 JS 也不能同时执行 ( 都修改 DOM 就冲突了)
      • webworker 支持多线程 ,但是不能 访问 DOM

单线程的解决方案异步

暴露出的问题

  • 问题一:没按照书写方式执行,可读性差
  • 问题二:callback 中不容易模块化

# 问题解答

  • 什么是单线程,和异步有什么关系?
    • 单线程就是同时只做一件事,两段 JS 不能同时 执行
    • 原因就是 为了避免 DOM 渲染的冲突
    • 异步是一种 “无奈” 的解决方案,虽然有很多问题

承上启下:

  • 单线程 - 同时间只能做一件事
  • 原因 - 避免 DOM 渲染冲突
  • 解决方案 - 异步
  • 异步的实现方式 - event loop

# 4-2 什么是 event-loop

主线程从 "任务队列" 中读取事件,这个过程是循环不断的,所以整个的这种运行机制又称为 Event Loop(事件循环)。 再谈 Event Loop

# 文字解释

event-loop

  • 事件轮询, JS 实现异步 的具体解决方案
  • 同步代码,直接执行
  • 异步函数先放在 异步队列 中
  • 待同步函数执行完毕,轮询执行 异步队列 的函数

# 实例分析

示例一:

mark

示例二:

mark

示例三:

mark

上图有 2 种结果:(a 是在 ajax 请求成功时放入异步队列,所以时间不确定)

  1. d -> c -> a -> b
  2. d -> c -> b -> a

# 问题解答

  • 什么是 event-loop?
    • 事件轮询, JS 实现异步 的具体解决方案
    • 什么是异步队列,何时被放入 异步队列
    • 轮询的过程

# 4-3 jQuery 的 Deferred

jQuery 的 deferred 对象详解

jQuery 1.5.0 版本开始引入的一个新功能 ----deferred 对象

针对的读者是那些已经具备 jQuery 使用经验的开发者。如果你想了解 jQuery 的基本用法,请阅读 阮一峰 编写的《jQuery 设计思想》《jQuery 最佳实践》

# 知识点

  • jQuery 1.5 的变化
  • 使用 jQuery Deferred
  • 初步引入 Promise 概念

注意 :不要以为所有的网站 都是 vue 和 React 开发的

# jQuery 1.5 之前

var ajax = $.ajax({
  url: "data.json",
  success: function () {
    console.log("success1");
    console.log("success2");
    console.log("success3");
  },
  error: function () {
    console.log("error");
  },
});
console.log(ajax); //返回一个 XHR 对象

# jQuery 1.5 之后

var ajax = $.ajax("data.json");
ajax
  .done(function () {
    console.log("success 1");
  })
  .fail(function () {
    console.log("error");
  })
  .done(function () {
    console.log("success 2");
  });

console.log(ajax); //返回一个 deferred 对象

# jQuery 1.5 的变化

  • 无法改变 JS 异步和单线程的本质
  • 只能从写法上杜绝 callback 这种形式
  • 它是一种语法糖形式,但是解耦了代码
  • 很好的体现:开放封闭原则

# 使用 jQuery Deferred

使用 jQuery Deferred

对比 说明

未使用情况:

// 给出一段非常简单的异步操作代码,使用 setTimeout 函数
var wait = function () {
  var task = function () {
    console.log("执行完成");
  };
  setTimeout(task, 2000);
};
wait();
// 新增需求:要在执行完成之后进行某些特别复杂的操作,代码可能会很多,而且分好几个步骤

使用 jQuery Deferred

function waitHandle() {
  var dtd = $.Deferred(); //创建一个 Deferred 对象

  var wait = function (dtd) {
    //要求传入一个 Deferred 对象
    var task = function () {
      console.log("执行完成");
      dtd.resolve(); //表示异步任务已经完成
      // dtd.reject()  //表示异步任务失败或出错
    };
    setTimeout(task, 2000);
    return dtd; // 要求返回 Deferred 对象
  };

  // 注意,这里一定要有返回值
  return wait(dtd);
}

//区别于promise,好像不能链式调用
var w = waitHandle();
w.then(
  function () {
    console.log("ok 1");
  },
  function () {
    console.log("err 1");
  }
);

w.then(
  function () {
    console.log("ok 2");
  },
  function () {
    console.log("error 2");
  }
);

// 还有 w.done w.fail

# 总结

  • 总结,dtd 的 API 可分成两类,用意不同
  • 第一类:dtd.resolve dtd.reject
  • 第二类:dtd.then dtd.done dtd.fail
  • 这两类应该分开,否则后果很严重

# 问题解答

  • 是否用过 jQuery 的 Deferred

    • 可以 jQuery 1.5 对 ajax 的改变举例
    • 说明如何简单的封装,使用 Deferred
    • 说明 ES6 promise 和 Deferred 的区别

想要深入理解它,就需要知道它的前世今生。

# 4-4 Promise

Promise 是异步编程的一种解决方案,比传统的解决方案 —— 回调函数和事件 —— 更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了 Promise 对象。

所谓 Promise ,简单说就是一个容器,里面保存着某个未来才会结束的事件(通常是一个异步操作)的结果。从语法上说,Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。

ECMAScript 6 Promise 对象

基础语法 请参考我的另一篇博客: Promise 详细分析

Promise 对象有以下两个特点

  1. 对象的状态不受外界影响。 Promise 对象代表一个异步操作,有三种状态: pending (进行中)、 fulfilled (已成功)和 rejected (已失败)。只有异步操作的结果,可以决定当前是哪一种状态,任何其他操作都无法改变这个状态。这也是 Promise 这个名字的由来,它的英语意思就是 “承诺”,表示其他手段无法改变。
  2. 一旦状态改变,就不会再变,任何时候都可以得到这个结果。 Promise 对象的状态改变,只有两种可能:从 pending 变为 fulfilled 和从 pending 变为 rejected 。只要这两种情况发生,状态就凝固了,不会再变了,会一直保持这个结果,这时就称为 resolved(已定型)。如果改变已经发生了,你再对 Promise 对象添加回调函数,也会立即得到这个结果。这与事件(Event)完全不同,事件的特点是,如果你错过了它,再去监听,是得不到结果的。

注意,为了行文方便,本章后面的 resolved 统一只指 fulfilled 状态,不包含 rejected 状态。

有了 Promise 对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外, Promise 对象提供统一的接口,使得控制异步操作更加容易。

Promise 也有一些缺点。首先,无法取消 Promise ,一旦新建它就会立即执行,无法中途取消。其次,如果不设置回调函数, Promise 内部抛出的错误,不会反应到外部。第三,当处于 pending 状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。

如果某些事件不断地反复发生,一般来说,使用 Stream 模式是比部署 Promise 更好的选择。

# 异常捕获

function loadImg(src) {
  const promise = new Promise(function (resolve, reject) {
    var img = document.createElement("img");
    img.onload = function () {
      resolve(img);
    };
    img.onerror = function () {
      reject();
    };
    img.src = src;
  });
  return promise;
}

var src =
  "https://edu-image.nosdn.127.net/B34DC36428D2D51B8EF5EE2C83CE9BF2.png?imageView&thumbnail=241y34&quality=100";
var result = loadImg(src);

//规定 : then 只接受一个参数,最后统一用 catch 捕获异常
result
  .then(function (img) {
    console.log(img.width);
    return img;
  })
  .then(function (img) {
    console.log(img.height);
  })
  .catch(function (ex) {
    // 最后统一 catch
    console.log(ex);
  });

# Promise.all & Promise.race

Promise.all 可以将多个 Promise 实例包装成一个新的 Promise 实例。同时,成功和失败的返回值是不同的,成功的时候返回的是一个结果数组,而失败的时候则返回最先被 reject 失败状态的值。

Promse.race 就是赛跑的意思,意思就是说,Promise.race ([p1, p2, p3]) 里面哪个结果获得的快,就返回那个结果,不管结果本身是成功状态还是失败状态。

理解和使用 Promise.all 和 Promise.race

mark

演示

function loadImg(src) {
  const promise = new Promise(function (resolve, reject) {
    var img = document.createElement("img");
    img.onload = function () {
      resolve(img);
    };
    img.onerror = function () {
      reject();
    };
    img.src = src;
  });
  return promise;
}

var src1 =
  "https://edu-image.nosdn.127.net/B34DC36428D2D51B8EF5EE2C83CE9BF2.png?imageView&thumbnail=241y34&quality=100";
var src2 = "https://www.imooc.com/static/img/index/logo.png";

var result1 = loadImg(src1);
var result2 = loadImg(src2);

Promise.all([result1, result2]).then(function (datas) {
  console.log(datas[0]);
  console.log(datas[1]);
});

//谁快谁先执行
Promise.race([result1, result2]).then(function (datas) {
  console.log(datas);
});

Promise.all 的执行结果

mark

Promise.race 的执行结果

mark

# promise 标准

  • 关于 “标准” 的闲谈
  • 状态变化
  • then

# 关于 “标准” 的闲谈

  • 任何技术推广使用都需要一套标准来支撑
  • 如 html js css http 等,无规矩不成方圆
  • 任何不符合标准的东西,终将会被用户抛弃
  • 不要挑战标准,不要自造标准

# 状态变化

  • 三种状态:pending fulfilled rejected
  • 初始状态:pending
  • 成功:pending 变成 fulfilled ,失败:pending 变成 rejected
  • 状态变化不可逆

# then

  • Promise 实例必须实现 then 这个方法

  • then () 必须可以接受两个函数作为参数

  • then () 返回的必须是一个 Promise 实例

    mark

# 问题解答

问题:Promise 的基本使用和原理?

  • 基本语法 (复习)
  • 如何异常捕获 ( error 和 reject 都要考虑)
  • 多个串联 - 链式执行的好处
  • Promise.all 和 Promise.race
  • Promise 标准 - 状态变化,then 函数

# 4-5 async/await

ES2017 标准引入了 async 函数,使得异步操作变得更加方便。

async 函数是什么?一句话,它就是 Generator 函数的语法糖。

ECMAScript 6 入门 async 函数

  • then 只是将 callback 拆分了
  • async/await 是最直接的同步写法

mark

最直接的同步写法:

const load = async function () {
  const result1 = await loadImg(src1);
  console.log(result1);
  const result2 = await loadImg(src2);
  console.log(result2);
};
load();

# 用法

  • 使用 await,函数必须用 async 标识
  • await 后面跟的是一个 Promise 实例
  • 需要 babel-polyfill
function loadImg(src) {
  const promise = new Promise(function (resolve, reject) {
    var img = document.createElement("img");
    img.onload = function () {
      resolve(img);
    };
    img.onerror = function () {
      reject();
    };
    img.src = src;
  });
  return promise;
}

var src1 =
  "https://edu-image.nosdn.127.net/B34DC36428D2D51B8EF5EE2C83CE9BF2.png?imageView&thumbnail=241y34&quality=100";
var src2 = "https://www.imooc.com/static/img/index/logo.png";

const load = async function () {
  const result1 = await loadImg(src1);
  console.log(result1);
  const result2 = await loadImg(src2);
  console.log(result2);
};
load();

# 问题解答

  • 介绍一下 async/await(ES7)(和 Promise 的区别、联系)
    • 基本语法
    • 使用了 Promise,并没有和 Promise 冲突
    • 完全是同步的写法,再也没有回调函数
    • 但是:改变不了 JS 单线程、异步的本质

原型

# 原型

本章将结合 jQueryzepto 源码来讲解原型的实际应用。通过 源码来分析 jQuery 和 zepto 是如何使用原型的,以及通过它们的 插件机制,讲解 原型的扩展性

《前端 JS 面试技巧》请参考我之前的博客: 前端 JS 基础面试技巧

# 关于原型

  • 《前端 JS 面试技巧》已经讲解过原型的 基础知识
  • 高级面试题,光会原型基础还不够,还要实际应用
  • zepto jquery 中如何用原型
  • 顺便也算是解读了 zepto 和 jquery 的部分源码

# 题目

  • 说一个原型的实际应用
  • 原型如何体现它的扩展性

# 原型的实际应用

原型的实际应用

# 知识点

  • jquery 和 zepto 的简单使用
  • zepto 如何使用原型
  • jquery 如何使用原型

# 简单使用

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
<p>jquery test 1</p>
<p>jquery test 2</p>
<p>jquery test 3</p>

<div id="div1">
    <p>jquery test in dev</p>
</div>

<script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
<script>
    var $p = $('p')
    $p.css('font-size', '40px') //css 是原型方法
    alert($p.html()) 		    //html 是原型方法

    var $div1 = $('#div1')      //css 是原型方法
    $div1.css('color', 'blue')  //html 是原型方法
    alert($div1.html())
</script>
</body>
</html>

# zepto 如何使用原型

源码中,这里的处理情况比较复杂。但因为本次只针对原型,因此这里就弱化了

(function (window) {
  //空对象
  var zepto = {};

  zepto.init = function (selector) {
    //源码中,这里的处理情况比较复杂。但因为本次只针对原型,因此这里就弱化了
    var slice = Array.prototype.slice;
    var dom = slice.call(document.querySelectorAll(selector));
    return zepto.Z(dom, selector);
  };

  // 即使用 zepto 时候的 $
  var $ = function (selector) {
    return zepto.init(selector);
  };

  // 这就是构造函数
  function Z(dom, selector) {
    var i,
      len = dom ? dom.length : 0;
    for (i = 0; i < len; i++) this[i] = dom[i];
    this.length = len;
    this.selector = selector;
  }

  zepto.Z = function (dom, selector) {
    // 注意,出现了 new 关键字
    return new Z(dom, selector);
  };

  $.fn = {
    constructor: zepto.Z,
    css: function (key, value) {},
    html: function (value) {},
  };

  zepto.Z.prototype = Z.prototype = $.fn;

  window.$ = $;
})(window);

# jquery 如何使用原型

简化了源码,重在讲解 jquery 如何使用原型

//简化了源码,真实源码分支很多,选取部分代码
(function (window) {
  var jQuery = function (selector) {
    //注意 new 关键字,第一步就找到了 构造函数
    return new jQuery.fn.init(selector);
  };

  //定义构造函数
  var init = (jQuery.fn.init = function (selector) {
    var slice = Array.prototype.slice;
    var dom = slice.call(document.querySelectorAll(selector));

    var i,
      len = dom ? dom.length : 0;
    for (i = 0; i < len; i++) this[i] = dom[i];
    this.length = len;
    this.selector = selector || "";
  });

  init.prototype = jQuery.fn;

  //初始化 jQuery.fn
  jQuery.fn = jQuery.prototype = {
    constructor: jQuery,

    //其他函数...
    css: function (key, value) {},
    html: function (value) {},
  };

  //定义原型
  init.prototype = jQuery.fn;
  window.$ = jQuery;
})(window);

# 问题解答

  • 描述一下 jquery 如何使用原型
  • 描述一下 zepto 如何使用原型
  • 再结合自己的项目经验,说一下自己开发的例子

# 如何体现原型的扩展性

体现原型的扩展性

# 知识点

  • 总结 zepto 和 jquery 原型的使用
  • 插件机制

# 总结 zepto 和 jquery 原型的使用

总结 zepto 和 jquery 原型的使用

相关 代码实现 请看上文 :zepto 如何使用原型 和 jquery 如何使用原型

问题一:为何要把原型方法放在 $.fn ?

//初始化 jQuery.fn
jQuery.fn = jQuery.prototype = {
  constructor: jQuery,

  //其他函数...
  css: function (key, value) {},
  html: function (value) {},
};

//定义原型
init.prototype = jQuery.fn;

解答因为要扩展插件 ,做一个简单的插件的例子

// 因为要扩展插件,做一个简单的插件的例子
$.fn.getNodeName = function () {
  return this[0].nodeName;
};

好处

  1. 只有 $ 会暴露在 window 全局变量
  2. 将插件扩展统一到 $.fn.xxx 这一接口,方便使用

# 总结

  • 说一个原型的实际应用

    • 描述一下 jquery 如何使用 原型
    • 描述一下 zepto 如何使用 原型
    • 再结合自己的项目经验,说一下自己开发的例子
  • 原型如何体现它的扩展性

    • 说一下 jquery 和 zepto 的 插件机制
    • 结合自己的开发经验,做过的基于原型的插件

ES6 语法

# ES6 语法

本章主要讲解工作中最常用的 ES6 语法,包括 Module Class Promise 等语法,还会介绍使用 babel webpack rollup 来搭建 ES6 编译环境。

ECMAScript 6 入门

# ES6 使用

  • 开发环境已经普及使用
  • 浏览器环境却支持不好( 需要开发环境编译 )
  • 内容很多,重点了解常用语法
  • 面试:开发环境的使用 + 重点语法的掌握

# 问题

  • ES6 模块化如何使用,开发环境如何打包
  • Class 和 普通构造函数 有何区别 ?
  • Promise 的基本使用和原理
  • 总结一下 ES6 其它常用功能

# 2-1 ES6 模块化语法

ES6 模块化如何使用,开发环境如何打包

# 知识点

ES6 模块化如何使用,开发环境如何打包:

  • 模块化的基本语法
  • 开发环境的配置
  • 关于 JS 众多 模块化标准

# export 语法

export 语法

/*util1.js*/
export default {
  a: 100,
};

/*util2.js*/
export function fn1() {
  alert("fn1");
}
export function fn2() {
  alert("fn2");
}

/*index.js*/
import util1 from "./util1.js";
import { fn1, fn2 } from "./util2.js";

console.log(util1);
fn1();
fn2();

# 开发环境 - babel

Babel 是一个 JavaScript 编译器。官网

使用技巧 可参考 React 学习第一天 :webpack 中使用 Babel 配置

Vue 第六天学习 :webpack 中 babel 的配置

# 开发环境 - webpack

开发环境 - webpack - webpack 是一个模块打包器 官网

详情 可参考我的博客:Vue 第五天:webpackVue 第六天学习:深入 webpack 以及 React 学习第一天:创建基本的 webpack4.x 项目

# rollup.js

概述 (Overview) 中文文档 官网

Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验

rollup 功能单一(极致) ,webpack 功能强大

# 关于 JS 众多 模块化标准

关于 JS 众多 模块化标准

详情可参考我的博客:开发环境: 模块化

发展过程

  • 没有模块化
  • AMD 成为 标准,require.js (也有 CMD【用的不多 】)
  • 前端打包工具,nodejs 模块化可以被使用
  • ES6 出现 ,想统一现在所有的模块化标准
  • nodejs 积极支持,浏览器尚未统一
  • 你可以自造 lib,但是不要自造标准

# 问题解答

  • 语法:import export (注意有无 default)
  • 环境:babel 编译 ES6 语法,模块化可用 webpack 和 rollup
  • 扩展:说一下自己对模块化标准统一的期待

# 2-2 Class 和 普通构造函数有何区别

Class 和 普通构造函数有何区别

我的博客:React 学习第二天:了解 class

# 知识点

  • JS 构造函数
  • Class 基本语法
  • 语法糖
  • 继承

# 问题解答

  • Class 和 普通构造函数 有何区别 ?
    • Class 在语法上更加贴合面向对象的写法
    • Class 实现继承更加易读、易理解
    • 更易于写 java 等后端语言的使用
    • 本质还是语法糖, 使用 prototype

# 2-3 Promise 的基本使用

# Promise 的基本使用

可参考我的博客:Promise 详细分析

  • Callback Hell
  • Promise 语法

# Callback Hell

function loadImg(src, callback, fail) {
  var img = document.createElement("img");
  img.onload = function () {
    callback(img);
  };
  img.onerror = function () {
    fail();
  };
  img.src = src;
}

var src =
  "https://edu-image.nosdn.127.net/B34DC36428D2D51B8EF5EE2C83CE9BF2.png?imageView&thumbnail=241y34&quality=100";
loadImg(
  src,
  function (img) {
    console.log(img.width);
  },
  function () {
    console.log("failed");
  }
);

# Promise 语法

function loadImg(src) {
  const promise = new Promise(function (resolve, reject) {
    var img = document.createElement("img");
    img.onload = function () {
      resolve(img);
    };
    img.onerror = function () {
      reject();
    };
    img.src = src;
  });
  return promise;
}

var src =
  "https://edu-image.nosdn.127.net/B34DC36428D2D51B8EF5EE2C83CE9BF2.png?imageView&thumbnail=241y34&quality=100";
var result = loadImg(src);

result.then(
  function (img) {
    console.log(img.width);
  },
  function () {
    console.log("faild");
  }
);

result.then(function (img) {
  console.log(img.height);
});

# 问题解答

  • Promise 的基本使用和原理?
    • new Promise 实例,而且要 return
    • new Promise 时要传入函数,函数有 resolve, reject 两个参数
    • 成功时执行 resolve () 失败时执行 reject ()
    • then 监听结果

# 2-4 总结一下 ES6 其它常用功能

# 知识点

  • let/const
  • 多行字符串 / 模板变量
  • 解构赋值
  • 块级作用域
  • 函数默认参数
  • 箭头函数

# let/const

mark

# 多行字符串 / 模板变量

mark

# 解构赋值

mark

# 块级作用域

mark

# 函数默认参数

mark

# 箭头函数

mark

mark

  • Copyrights © 2015-2021 zhou chen
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信