清空或截断数组
const arr = [11, 22, 33, 44, 55, 66];
// truncanting
arr.length = 3;
console.log(arr); // => [11, 22, 33]
//clearing
arr.length = 0;
console.log(arr); // => []
console.log(arr[2]); // => undefined
JS中使用 typeof 能得到的哪些类型? 考点:JS变量类型
何时使用 === 何时使用 == ? 考点:强制类型转换
window.onload和DOMContentLoaded的区别? 考点:浏览器渲染过程
用JS创建10个标签,点击的时候弹出来对应的序号 考点:作用域
简述如何实现一个模块加载器,实现类似require.js的基本功能 考点:JS模块化
实现数组的随机排序 考点:JS基础算法
变量类型和计算
题目
JS中使用 typeof 能得到的哪些类型? 考点:JS变量类型
何时使用 === 何时使用 == ? 考点:强制类型转换
JS中有哪些内置函数
JS变量按照存储方式区分为哪些类型,并描述其特点
如何理解JSON
解答
undefined、string、number、boolean、object、function
obj.a == null 除了判断null和undefined外,都需要使用全等符
数据封装类对象- Object、Array、Boolean、Number、String、Function、Date、RegExp、Error
值类型、引用类型
JSON 只不过是一个 JS 对象而已, 也是一个数据格式
知识点
变量类型
变量计算
值类型 vs 引用类型
typeof 运算符详解
字符串拼接
== 运算符
if语句
逻辑运算
题目
如何准确判断一个变量是数组类型
写一个原型链继承的例子
描述new一个对象的过程
zepto(或其他框架) 源码中如何使用原型链
解答
arr instanceof Array
// 动物 function Animal() { this.eat = function () { console.log('animal eat') } } // 狗 function Dog() { this.bark = function () { console.log('dog bark') } } Dog.prototype = new Animal() // 哈士奇 var hashiqi = new Dog() // 接下来代码演示时,会推荐更加贴近实战的原型继承示例!描述new 一个对象的过程
创建一个新对象
this 指向这个新对象
执行代码,即对this 赋值
返回 this
zepto (或其他框架) 源码中如何使用原型链
阅读源码是高效提高技能的方式
但不能 "埋头苦钻" 有技巧在其中
慕课网搜索 "zepto设计和源码分析"
知识点
构造函数
构造函数 - 扩展
原型规则和示例
原型链
instanceof
所有的引用类型(数组、对象、函数),都具有对象特性,即可自由扩展属性(除了 "null" 以外)
所有的引用(数组、对象、函数),都有一个__proto__ (隐式原型)属性,属性值是一个普通的对象
所有的函数,都有一个 prototype (显式原型) 属性,属性值也是一个普通对象
所有的引用类型(数组、对象、函数), __ proto __ 属性值指向它的构造函数的 "prototype" 属性值
var obj = {}; obj.a = 100; var arr = []; arr.a = 100; function fn () {} fn.a = 100; console.log(obj.__proto__); console.log(arr.__proto__); console.log(fn.__proto__); console.log(fn.prototype) console.log(obj.__proto__ === Object.prototype)当试图得到一个对象的某个属性时,如果这个对象本身没有这个属性,那么会去它的__ proto __ (即它的构造函数的 prototype) 中寻找。
// 构造函数 function Foo(name, age) { this.name = name; } Foo.prototype.alertName = function () { alert(this.name); } // 创建示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name); } // 测试 f.printName(); // zhangsan f.alertName(); // zhangsan var item; for (item in f) { //高级浏览器已经在 for in 中屏蔽了来自原型的属性 //但是这里建议大家还是加上这个判断,保证程序的健壮性 if (f.hasOwnProperty(item)) { console.log(item) } }//构造函数 function Foo(name, age) { this.name = name; } Foo.prototype.alertName = function () { alert(this.name); } // 创建示例 var f = new Foo('zhangsan') f.printName = function () { console.log(this.name) } // 测试 f.printName() f.alertName() f.toString() // 要去 f.__proto__.__proto__中查找instanceof
用于判断 引用类型 属于哪个 构造函数 的方法
f instanceof Foo 的判断逻辑是:
f的 __ proto __ 一层一层往上,能否对应到 Foo.prototype
再试着判断 f instanceof Object
题目
说一下对变量提升的理解
变量定义
函数声明(注意和函数表达式的区别)
说明 this 几种不同的使用场景
作为构造函数执行
作为对象属性执行
作为普通函数执行
call apply bind
创建10个标签,点击的时候弹出来对应的序号
// 这是一个错误的写法!!! var i,a for (i = 0; i < 10; i++) { a = document.createElement('a') a.innerHTML = i + '<br>' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) } // 这是正确的写法!!! var i for (i = 0; i < 10; i++) { (function(i){ var a = document.createElement('a') a.innerHTML = i + '<br>' a.addEventListener('click', function (e) { e.preventDefault() alert(i) }) document.body.appendChild(a) })(i) }
如何理解作用域
自由变量
作用域链,即自由变量的查找
闭包的两个场景
实际开发中闭包的应用
// 闭包实际应用中主要用于封装变量,收敛权限 function isFirstLoad() { var _list = [] return function (id) { if (_list.indexOf(id) >= 0) { return false } else { _list.push(id) return true } } } // 使用 var firstLoad = isFirstLoad() firstLoad(10) // true firstLoad(10) // false firstLoad(20) // true知识点
执行上下文
this
作用域
作用域链
闭包
console.log(a) // undefined var a = 100 fn('zhangsan') // 'zhangsan' 20 function fn(name) { age = 20 console.log(name, age) var age }范围:一段
全局: 变量定义、函数声明 ==一段
函数: 变量定义、函数声明、this、arguments ==函数==
PS:注意"函数声明"和"函数表达式"的区别
this 要在执行时才能确认值,定义时无法确认
var a = { name: 'A', fn: function () { console.log(this.name) } } a.fn() // this === a a.fn.call({name: 'B'}) // this === {name: 'B'} var fn1 = a.fn fn1() // this === window作为构造函数执行
作为对象属性执行
作为普通函数执行
call apply bind
没有块级作用域
只有函数和全局作用域
// 无块级作用域 if (true) { var name = 'zhangsan' } console.log(name) // 函数和全局作用域 var a = 100 function fn() { var a = 200 console.log('fn', a) } console.log('global', a) fn()//作用域链 var a = 100 function fn() { var b = 200 // 当前作用域没有定义的变量,即 "自由变量" console.log(a) console.log(b) } fn()function F1() { var a = 100 // 返回一个函数 (函数作为返回值) return function () { console.log(a) } } // f1 得到一个函数 var f1 = F1() var a = 200 f1()函数作为返回值
函数作为参数传递
题目
同步和异步的区别是什么?分别举一个同步和异步的例子
同步会阻塞代码执行,而异步不会
alert是同步,setTimeout是异步
一个关于setTimeout的笔试题
console.log(1) setTimeout(function () { console.log(2) }, 0) console.log(3) setTimeout(function () { console.log(4) }, 1000) console.log(5)前端使用异步的场景有哪些
定时任务:setTimeout, setInverval
网络请求:ajax请求,动态加载
事件绑定
知识点
什么是异步(对比同步)
前端使用异步的场景
异步和单线程
// 异步-不阻塞 console.log(100) setTimeout(function() { console.log(200) }, 1000) console.log(300) // 同步-阻塞 console.log(100) alert(200) console.log(300)在可能发生等待的情况
等待过程中不能像 alert 一样阻塞程序运行
因此,所有的 "等待的情况" 都需要异步
定时任务:setTimeout, setInverval
网络请求:ajax请求,动态加载
事件绑定
// ajax请求代码示例 console.log('start') $.get('./data1.json', function(data1) { console.log(data1) }) console.log('end') // <img>加载示例 console.log('start') var img = document.createElement('img') img.onload = function(){ console.log('loaded') } img.src = '/xxx.png' console.log('end') // 事件绑定示例 console.log('start') document.getElementById('btn1').addEventListener('click', function() { alert('clicked') }) console.log('end')执行第一行,打印100
执行setTimeout后,传入setTimeout的函数会被暂存起来,不会立即执行(单线程的特点,不能同时干两件事)
执行最后一行,打印300
待所有程序执行完,处于空闲状态时,会立马看有没有暂存起来的要执行。
发现暂存起来的setTimeout中的函数无需等待时间,就立即拿过来执行
题目
获取 2017-06-10 格式的日期
function formatDate(dt) { if (!dt) { dt = new Date() } var year = dt.getFullYear() var month = dt.getMonth() + 1 var date = dt.getDate() if (month < 10) { // 强制类型转换 month = '0' + month } if (date < 10) { // 强制类型转换 date = '0' + date } // 强制类型转换 return year + '-' + month + '-' + date } var dt = new Date() var formatDate = formatDate(dt) console.log(formatDate)获取随机数,要求是长度一致的字符串格式
var random = Math.random() var random = random + '0000000000' // 后面加上10个零 var random = random.slice(0, 10) console.log(random)写一个能遍历对象和数组的通用forEach函数
function forEach(obj, fn) { var key if (obj instanceof Array) { // 准确判断是不是数组 obj.forEach(function (item, index) { fn(index, item) }) } else { // 不是数组就是对象 for (key in obj) { fn(key, obj[key]) } } } var arr = [1,2,3] // 注意,这里参数的顺序换了,为了和对象的遍历格式一致 forEach(arr, function (index, item) { console.log(index, item) }) var obj = {x: 100, y: 200} forEach(obj, function (key, value) { console.log(key, value) })知识点
日期
Math
数组API
对象API
Date.now() // 获取当前时间毫秒数 var dt = new Date() dt.getTime() // 获取毫秒数 dt.getFullYear() //年 dt.getMonth // 月 (0-11) dt.getDate() // 日 (0-31) dt.getHours() // 小时 (0-23) dt.getMinutes() // 分钟 (0-59) dt.getSeconds() // 秒 (0-59)获取随机数 Math.random()
forEach 遍历所有元素
every 判断所有元素是否都符合条件
some 判断是否有至少一个元素符合条件
sort 排序
map 对元素重新组装,生成新数组
filter 过滤符合条件的元素
var arr = [1,2,3] arr.forEach(function (item, index) { // 遍历数组的所有元素 console.log(index, item) })var arr = [1,2,3] var result = arr.every(function (item, index) { // 用来判断所有的数组元素,都满足一个条件 if (item < 4) { return true } }) console.log(result)var arr = [1,2,3] var result = arr.some(function (item, index) { // 用来判断所有的数组元素,只要有一个满足条件即可 if (item < 2) { return true } }) console.log(result)var arr = [1,4,2,3,5] var arr2 = arr.sort(function(a, b) { // 从小到大排序 return a - b // 从大到小排序 // return b - a }) console.log(arr2)var arr = [1,2,3,4] var arr2 = arr.map(function(item, index) { // 将元素重新组装,并返回 return '<b>' + item + '</b>' }) console.log(arr2)var arr = [1,2,3,4] var arr2 = arr.filter(function(item, index) { // 通过某一个条件过滤数组 if (item >= 2) { return true } }) console.log(arr2)var obj = { x: 100, y: 200, z: 300 } var key for (key in obj) { // 注意这里的 hasOwnProperty, 再讲原型链时候讲过了 if (obj.hasOwnProperty(key)) { console.log(key, obj[key]) } }JS基础知识: ECMA 262 标准
JS-Web-API: W3C 标准
W3C标准中关于JS的规定有:
DOM操作
BOM操作
事件绑定
ajax请求(包括http协议)
存储
题目
DOM 是哪种基本的数据结构?
树
DOM 操作的常用API有哪些
获取DOM节点,以及节点的property和Attribute
获取父节点,
新增节点,删除节点
DOM 节点的Attribute 和 property有何区别
property 只是一个JS对象的属性的修改
Attribute 是对html标签属性的修改
知识点
DOM本质
DOM节点操作
DOM结构操作
获取DOM节点
property
Attribute
var div1 = document.getElementById('div1') // 元素 var divList = document.getElementsByTagName('div') // 集合 console.log(divList.length) console.log(divList[0]) var containerList = document.getElementsByClassName('.container') // 集合 var pList = document.querySelectorAll('p') // 集合// property var pList = document.querySelectorAll('p') var p = pList[0] console.log(p.style.width) // 获取样式 p.style.width = '100px' console.log(p.className) p.className = 'p1' // 获取 nodeName 和 nodeType console.log(p.nodeName) console.log(p.nodeType)// Attribute var pList = document.querySelectorAll('p') var p = pList[0] p.getAttribute('data-name') p.setAttribute('data-name', 'sss') p.getAttribute('style') p.setAttribute('style', 'font-size:28px;')新增节点
获取父元素
获取子元素
删除节点
// 新增节点 var div1 = document.getElementById('div1') // 添加新节点 var p1 = document.createElement('p') p1.innerHtml = 'this is p1' div1.appendChild(p1) // 添加新创建元素 // 移动已有节点 var p2 = document.getElementById('p2') div1.appendChild(p2)// 获取父元素和子元素 var div1 = document.getElementById('div1') var parent = div1.parentElement var child = div1.childNodes div1.removeChild(child[0])题目
如何检测浏览器的类型
var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome)拆解url的各部分
location.href location.protocol location.pathname location.search location.hash知识点
navigator
screen
location
history
// navigator var ua = navigator.userAgent var isChrome = ua.indexOf('Chrome') console.log(isChrome) // screen console.log(screen.width) console.log(screen.height)// location console.log(location.href) console.log(location.protocol) // 'http:' 'https:' console.log(location.pathname) // user/api console.log(location.search) console.log(location.hash) // history history.back() history.forward()题目
编写一个通用的事件监听函数
描述事件冒泡流程
对于一个无限下拉加载图片的页面,如何给每个图片绑定事件
知识点
通用事件绑定
事件冒泡
代理
var btn = document.getElementById('btn1') btn.addEventListener('click', function (event) { console.log('clicked') }) function bindEvent(elem, type, fn) { elem.addEventListener(type, fn) } var a = document.getElementById('link1') bindEvent(a, 'click', function(e) { e.preventDefault() // 阻止默认行为 alert('clicked') })IE低版本使用 attachEvent绑定事件,和W3C标准不一样
IE低版本使用量以非常少,很多网站都早已不支持
建议对IE低版本的兼容性: 了解即可,无需深究
如果遇到对IE低版本要求苛刻的面试,果断放弃
var div1 = document.getElementById('div1') div1.addEventListener('click', function(e) { var target = e.target if (target.nodeName === 'A') { alert(target.innerHTML) } })function bindEvent(elem, type, selector, fn) { if (fn == null) { fn = selector selector = null } elem.addEventListener(type, function (e) { var target if (selector) { target = e.target if (target.matches(selector)) { fn.call(target, e) } } else { fn(e) } }) }代码简洁
减少浏览器内存占用
题目
手动编写一个ajax, 不依赖第三方库
跨域的几种实现方式
知识点
XMLHttpRequest
状态码说明
跨域
var xhr = new XMLHttpRequest() xhr.open("GET", "/api", false) xhr.onreadystatechange = function () { // 这里的函数异步执行,可参考之前JS基础中的异步模块 if (xhr.readyState == 4) { if (xhr.status == 200) { alert(xhr.responseText) } } } xhr.send(null)IE低版本使用 ActiveXObject, 和W3C标准不一样
IE低版本使用量以非常少,很多网站都早已不支持
建议对IE低版本的兼容性: 了解即可,无需深究
如果遇到对IE低版本要求苛刻的面试,果断放弃
0 - (未初始化)还没有调用send()方法
1 - (载入)已调用send()方法,正在发送请求
2 - (载入完成)send()方法执行完成,已经接收到全部响应内容
3 - (交互) 正在解析响应内容
4 - (完成) 响应内容解析完成,可以在客户端调用了
2xx - 表示成功处理请求。如200
3xx - 需要重定向,浏览器直接跳转
4xx - 客户端请求错误,如404
5xx - 服务器端错误
什么是跨域
JSONP
服务器端设置 http header (cors)
浏览器有同源策略,不允许ajax访问其他域接口
跨域条件:协议、域名、端口、有一个不同就算跨域
但是有三个标签允许跨域加载资源
三个标签的场景
用于打点统计,统计网站可能是其他域
跨域注意事项
所有的跨域请求都必须经过信息提供方允许
如果未经允许即可获取,那是浏览器同源策略出现漏洞
不一定服务器端真正有一个 classindex.html 文件
服务器可以根据请求,动态生成一个文件,返回
同理于
存储
题目
请描述一下 cookie, sessionStorage 和 localStorage的区别?
容量
是否会携带到ajax中
API易用性
知识点
cookie
localStorage 和 sessionStorage
本身用于客户端和服务器端通信
但是它有本地存储的功能,于是就被 "借用"
使用 document.cookie = .... 获取和修改即可
存储量太小,只有4kb
所有http请求都带着,会影响获取资源的效率
API简单,需要封装才能用 document.cookie = ....
HTML5专门为存储而设计,最大容量5M
API简单易用
localStorage.setItem(key, value); localStorage.getItem(key);
IOS safari 隐藏模式下
localStorage.getItem 会报错
建议统一使用 try-catch 封装
面试官想通过开发环境了解面试者的经验
开发环境,最能提现工作产出的效率
知识点
不使用模块化的情况
使用模块化
AMD
CommonJS
题目
从输入url到得到html的详细过程
window.onload 和 DOMContentLoaded的区别
知识点
加载资源的形式
加载一个资源的过程
浏览器渲染页面的过程
输入url(或跳转页面)加载html
加载html中的静态资源
浏览器根据DNS服务器得到域名的IP地址
向这个IP的机器发送http 请求
服务器收到、处理并返回http 请求
浏览器得到返回内容
答案
根据HTML结构生成DOM Tree
根据CSS生成 CSSOM
将DOM和CSSOM整合形成RenderTree
根据 RenderTree 开始渲染和展示
遇到
window.onload // 页面的全部资源加载完才会执行,包括图片、视频等 DOMContentLoaded // DOM渲染完即可执行,此时图片、视频还可能没有加载完
原则
多使用内存、缓存或者其他方法
减少CPU计算、减少网络
从哪里入手
加载页面和静态资源
页面渲染
加载资源优化
静态资源的压缩合并
静态资源缓存
使用CDN让资源加载更快
使用SSR后端渲染,数据直接输出到HTML中
渲染优化
CSS放前面,JS放后面
懒加载(图片懒加载、下拉加载更多)
减少DOM查询,对DOM查询做缓存
减少DOM操作,多个操作尽量合并在一起执行
事件节流
尽早执行操作(如DOMContentLoaded)
//合并DOM插入 var listNode = document.getElementById('list') // 要插入10个 li 标签 var frag = document.createDocumentFragment() // 不触发DOM操作 var x, li; for (x = 0; x < 10; x++) { li = document.createElement("li") li.innerHTML = "list item " + x frag.appendChild(li) } listNode.appendChild(frag) //触发DOM操作var textarea = document.getElementById('text') var timeoutId textarea.addEventListener('keyup', function () { if (timeoutId) { clearTimeout(timeoutId) } timeoutId = setTimeout(function () { // 触发 change 事件 }, 100) })知识点
XSS跨站请求攻击
XSRF 跨站请求伪造
在新浪博客写一篇文章,同时偷偷插入一段
攻击代码中,获取cookie, 发送自己的服务器
发布博客,有人查看博客内容
会把查看者的cookie发送到攻击者的服务器
预防
前端替换关键字,例如替换 为>
后端替换
XSRF
你已登录一个购物网站,正在浏览器商品
该网站付费接口是xxx.com/pay?id=100 但是没有任何验证
然后你收到一封邮件,隐藏着 src=xxx.com/pay?id=100
>
你查看邮件的时候,就悄悄付费。
预防
增加验证流程,如输入指纹、密码、短信验证码
GUI渲染线程
javascript引擎线程
HTTP请求线程
浏览器事件触发线程
DOM Events
XMLHttpRequest(ajax)
fetch
WebSockets
Service Worker
Timer
异步处理方案
异步数据的链式处理
异步代码同步化
catch 获取异常
then 正常结束
事件轮巡
异步队列
dtd的API可分成两类,用意不同
第一类:dtd.resolve dtd.reject
第二类:dtd.then dtd.done dtd.fail
这两类应该分开,否则后果严重
使用 返回dtd.promise() 解决随意在监听范围调用reject
ES7提案
then 只是将 callback拆分了
async/await 是最直接的同步写法
用法
使用 await , 函数必须用async标识
await 后面跟的是一个Promise实例
需要babel-polyfill
问题
v-dom 是什么?
v-dom 如何运用?
介绍Diff算法
v-dom 是什么?
用js模拟dom结构
如果dom有变化,对比出变化的地方,只修改这个变化的地方
提高重绘性能
JS是前端中唯一:图灵完备语言
将DOM对比操作放在JS层,提高效率
遇到的问题
DOM操作是 "昂贵"的,js运行效率高
尽量减少DOM操作,而不是 "推倒重来"
项目越复杂,影响越严重
vdom即可解决这个问题
实现vdom的开源库
vue2.0借用了snabbdom
h函数、patch函数
h函数返回v-node
patch函数将vnode patch到一个真实的dom容器中
问题
什么是diff算法?
vdom为何用diff算法?
diff算法的实现过程
vdom为何使用diff算法
答案:
DOM操作是 "昂贵"的,因此尽量减少DOM操作
找出本次DOM必须更新的节点来更新,其他的不更新
这个 "找出" 的过程,就需要diff算法
diff算法的实现过程
patch(container, vnode)
patch(vnode, newVnode)
什么是diff算法?
是linux的基础命令 diff git diff
{ tag: 'ul', attrs: { id: 'list' }, children: [ { tag: 'li', attrs: { className: 'item' }, children: ['Item 1'] } ] }问题
如何理解MVVM?
如何实现MVVM?
是否解读过vue的源码?
题目
说一下使用jQuery和使用框架的区别?
说一下对MVVM的理解?
vue中如何实现响应式?
vue中如何解析模板?
vue的整个实现流程?
从jQuery到框架
数据和视图的分离,解耦(开放封闭原则)
以数据驱动视图,只关心数据变化,DOM操作被封装
如何理解 MVVM
MVVM - Model View ViewModel
三者之间的联系,以及如何对应到各段代码
ViewModel的理解,联系View和Model
响应式:vue如何监听到data的每个属性变化?
模板引擎:vue的模板如何被解析,指令如何处理?
渲染:vue的模板如何被渲染成html?以及渲染过程
什么是响应式
Object.defineProperty
模拟
什么是响应式
修改data属性之后,vue立刻监听到
data属性被代理到vm上
问题解答
关键是理解 Object.defineProperty
将data的属性代理到vm上
模板是什么
render函数
render函数与vdom
模板是什么?
本质:字符串
有逻辑,如v-if v-for等
与html格式很像,但有很大区别
最终还要转换为html来显示
模板最终必须转换成JS代码,因为:
有逻辑(v-if v-for),必须用JS才能实现(图灵完备)
转换为html渲染页面,必须用JS才能实现
因此,模板最终要转换成一个JS函数(render函数)
render 函数 - with的用法
var obj = { name: 'zhangsan', age: 20, getAddress: function () { alert('beijing') } } function fn() { with(obj){ console.log(name) console.log(age) getAddress() } }从哪里可以看到render函数?
复杂一点的例子,render函数是什么样子的?
v-if v-for v-on 都是怎么处理的?
vm._c其实就是snabbvdom的h
解答
模板:字符串,有逻辑,嵌入JS变量.....
模板必须转换为JS代码(有逻辑、渲染html、JS变量)
render函数是什么样子的
render函数执行是返回vnode
updateComponent
模板中的所有信息都被render函数包含
第一步:解析模板成render函数
第二步:响应式开始监听
第三步:首次渲染,显示页面,且绑定依赖
初次渲染,执行updateComponent,执行vm._render()
执行render函数,会访问到vm.list和vm.title
会被响应式的get方法监听到
执行updateComponent,会走到vdom的patch方法
patch将vnode渲染成DOM,初次渲染完成
为何要监听get,直接监听set不行吗?
data中有很多属性,有些被用到,有些可能不被用到
被用到的会走到get,没用到就不会走get
第四步:data属性变化,触发re-render
题目
说一下对组件化的理解
JSX本质是什么?
JSX和vdom的关系
说一下setState的过程
阐述自己对React和Vue的认识
组件的封装
组件的复用
组件的封装
视图
数据
变化逻辑(数据驱动视图变化)
组件的复用
props传递
解答
组件的封装: 封装视图、数据、变化逻辑
组件的复用: props传递、复用
JSX语法
JSX解析成JS
独立的标准
JSX语法
html形式
引入JS变量和表达式
if...else
循环
style和className
事件
JSX语法根本无法被浏览器所解析
那么它如何在浏览器运行?
JSX 解析
JSX其实是语法糖
开发环境会将JSX编译成JS代码
JSX的写法大大降低了学习成本和编码工作量
同时,JSX也会增加debug成本
JSX独立的标准
JSX是React引入的,但不是React独有的
React已经将它作为一个独立标准开放,其他项目也可用
React.createElement 是可以自定义修改的
说明:本身功能已经完备;和其他标准兼容和扩展性没问题
另:有机会录制>,就用JSX标准
问题解答
JSX语法(标签、JS表达式、判断、循环、事件绑定)
JSX是语法糖,需被解析成JS才能运行
JSX是独立的标准,可被其他项目使用
分析:为何需要vdom
React.createElement和h
何时patch?
自定义组件的解析
为何需要vdom
vdom 是 React初次推广开来的,结合JSX
JSX就是模板,最终要渲染成html
初次渲染 + 修改 state 后的 re-render
正好符号vdom的应用场景
React.createElement和h
初次渲染 - ReactDOM.render(,container)
会触发patch(container,vnode)
re-render - setState
会触发patch(vnode,newVnode)
自定义组件的解析
'div' - 直接渲染
即可,vdom可以做到
Input和List,是自定义组件(class),vdom默认不认识
因此Input和List定义的时候必须声明render函数
根据props初始化实例,然后执行实例的render函数
render函数返回的还是vnode对象
问题解答
为何需要vdom: JSX需要渲染成html,数据驱动视图
React.createElement和h,都生成vnode
何时patch: ReactDOM.render(...) 和 setState
自定义组件的解析:初始化实例,然后执行render
setState的异步
vue修改属性也是异步
setState的过程
setState的异步
addTitle(title) { const currentList = this.state.list console.log(this.state.list) // ['a', 'b'] this.setState({ list:currentList.concat(title) // 'c' }) console.log(this.state.list) // ['a', 'b'] }setState 为何需要异步?
可能会一次执行多次 setState
你无法规定、限制用户如何使用setState
没必要每次setState 都重新渲染,考虑性能
即便是每次重新渲染,用户也看不到中间的效果
vue修改属性也是异步
效果、原因和setState一样
setState的过程
答案:
每个组件实例,都有renderComponent方法
执行renderComponent会重新执行实例的render
render函数返回newVnode,然后拿到preVnode
执行patch(preVnode,newVnode)
问题解答
setState的异步:效果、原因
vue修改属性也是异步:效果、原因
setState的过程:最终走到patch(preVnode,newVnode)
两者的本质区别
看模板和组件化的区别
两者共同点
总结问题答案
前言
文无第一武无第二,技术选型没有绝对的对与错
技术选型要考虑的因素非常多
作为面试者,要有自己的主见
和面试官的观点不一致没关系,只要能说出理由
两者的本质区别
vue - 本质是MVVM框架,由MVC发展而来
React - 本质是前端组件化框架,由后端组件化发展而来
但这并不妨碍他们两者都能实现相同的功能
模板的区别
vue - 使用模板 (最初由 angular 提出)
React - 使用JSX
模板语法上,我更加倾向于JSX
模板分离上,我更加倾向于vue
组件化的区别
React本身就是组件化,没有组件化就不是React
vue也支持组件化,不过是在MVVM上的扩展
查阅vue组件化的文档,洋洋洒洒很多(侧面反映)
对于组件化,我更加倾向于React,更加清晰
两者共同点
都支持组件化
都是数据驱动视图
问题解答
国内使用,首推vue。文档更易读、易学、社区够大
如果团队水平较高,推荐使用React。组件化和JSX
移动端占大部分流量,已经远远超过PC
一线互联网公司都有自己的App
这些App中有很大比例的前端代码(不要惊讶)
拿微信举例,你每天浏览微信的内容,多少是前端?
题目
hybrid是什么,为何用hybrid?
介绍一下hybrid更新和上线的流程?
hybrid和h5的主要区别
前端JS和客户端如何通讯?
hybrid 文字解释
存在价值,为何会用hybrid
webview
file:// 协议
hybrid实现流程
hybrid 文字解释
hybrid 即 "混合",即前端和客户端的混合开发
需前端开发人员和客户端开发人员配合完成
某些环节也可能涉及到server端
PS:不要以为自己是前端就可以不理会客户端的知识
hybrid存在价值
可以快速迭代更新「关键」(无需app审核,思考为何?)
体验流畅(和NA的体验基本类似)
减少开发和沟通成本,双端公用一套代码
webview
是app中的一个组件(app 可以有webview,也可以没有)
用于加载H5页面,即一个小型的浏览器内核
file:// 协议
其实在一开始接触html开发,就已经使用了file协议
只不过你当时没有 "协议" "标准"等这些概念
再次强调 "协议" "标准"的重要性!!!
file协议: 本地文件,快
http(s)协议: 网络加载,慢
hrbrid 具体实现
不是所有场景都适合使用hybrid:
使用NA: 体验要求极致,变化不频繁 (如头条的首页)
使用hybrid: 体验要求高,变化频繁 (如头条的新闻详情页)
使用h5: 体验无要求, 不常用 (如举报、反馈等页面)
实现
前端做好静态页面 (html js css), 将文件交给客户端
客户端拿到前端静态页面,以文件形式存储在app中
客户端在一个webview中,通过file协议加载前端文件(html js css)
具体实现 - 遗留问题
app发布之后,静态文件如何实时更新?
静态页面如何获取内容?
问题解答
hybrid 是客户端和前端的混合开发
hybrid 存在的核心意义在于快速迭代,无需审核
hybrid实现流程(图),以及webview和file协议
思考 (目的,可行途径)
要替换每个客户端的静态文件
只能客户端来做 (客户端是我们开发的)
客户端去server下载最新的静态文件
我们维护server的静态文件
完整流程
分版本,有版本号, 如201803211015
将静态文件压缩成zip包,上传到服务端
客户端每次启动,都去服务端检查版本号
如果服务端版本号大于客户端版本号,就去下载最新的zip包
下载完之后解压包,然后将现有文件覆盖
问题解答
掌握流程图
要点1:服务端的版本和zip包维护
要点2:更新zip包之前,先对比版本号
要点3:zip下载解压和覆盖
优点
体验更好,跟NA体验基本一致
可快速迭代,无需审核
缺点
开发成本高。联调、测试、查bug都比较麻烦
运维成本高。参考此前讲过的更新上线的流程
适用的场景
hybrid: 产品的稳定功能,体验要求高,迭代频繁
h5: 单次的运营活动(如 xx 红包) 或不常用功能
问题解答
优点:体验好,可快速迭代
缺点:开发成本高,运维成本高
适用的场景:hybrid适合产品型,h5适合运营型
回顾之前遗留的问题
JS和客户端通讯的基本形式
schema协议简介和使用
schema使用的封装
内置上线
之前遗留的问题
新闻详情页适用hybrid,前端如何获取新闻内容?
不能用ajax获取。第一 跨域,第二 速度慢
客户端获取新闻内容,然后JS通讯拿到内容,再渲染
JS和客户端通讯的基本形式
JS访问客户端能力,传递参数和回调函数
客户端通过回调函数返回内容
之前介绍了http(s) 和 file协议
schema协议 - 前端和客户端通讯的约定
/* 网上搜的微信的部分 schema 协议 */ weixin://dl/scan 扫一扫 weixin://dl/feedback 反馈 weixin://dl/moments 朋友圈 weixin://dl/settings 设置内置上线
将以上封装的代码打包,叫做invoke.js 内置到客户端
客户端每次启动webview,都默认执行invoke.js
本地加载,免去网络加载的时间,更快。
本地加载,没有网络请求,黑客看不到schema协议,更安全
问题解答
通讯的基本形式:调用能力,传递参数,监听回调
对schema协议的理解和使用
调用schema代码的封装
内置上线的好处:更快、更安全
热爱!
怎么证明?
看书
写博客
做开源
看书 - 手下不离书
构建知识体系的最好方式
自己买书,不要借书
看书有技巧
博客 - 合格程序员的必备
开源 - github 的 star 是硬通货
