赞
踩
.parent {
display: flex;
justify-content: center; //水平居中
align-items: center; //垂直居中
}
详情请戳:https://blog.csdn.net/a1056244734/article/details/106759185
有<section>、<header>、<footer>、<aside>、<nav>、<video>、<audio>、<canvas>等...
详细瞅这里:https://blog.csdn.net/a1056244734/article/details/106865698
iframe是一种框架,也是一种很常见的网页嵌入方式
iframe的优点:
iframe的缺点:
分析了这么多,现在基本上都是用Ajax来代替iframe,所以iframe已经渐渐的退出了前端开发。
简言之:百分比布局+媒体查询
Bootstrap响应式布局是利用其栅格系统,对于不同的屏幕采用不同的类属性。在开发中可以只写一套代码在手机平板,PC端都能使用,而不用考虑使用媒体查询(针对不同的设备分别写不同的代码)。Bootstrap的官方解释:Bootstrap提供了一套响应式、移动设备优先的流式栅格系统,随着屏幕或视口(viewport)尺寸的增加,系统会自动分为做多12列。
栅格系统用于通过一系列的行(row)与列(column)的组合来创建页面布局。
使用Bootstrap响应式布局
首先需要在head中引入meta标签,添加viewpirt属性,content中宽度等于设备宽度, initial-scale:页面首次被显示可见区域的缩放级别,取值1则页面按实际尺寸显示,无任何缩放;
maximum-scale:允许用户缩放到的最小比例;
user-scalable:用户是否可以手动缩放。代码如下:
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
<link rel="stylesheet" type="text/css" href="/stylesheets/bootstrap.min.css">
盒模型又被分为 IE盒模型 和 W3C标准盒模型 两种类型。
在 IE8+ 的浏览器中要使用哪个盒模型可以由 box-sizing 控制。
box-sizing: content-box //W3C标准盒模型,默认
box-sizing: border-box //IE盒模型
这两者的区别在于:
详情请戳:CSS 盒模型(Box Model),box-sizing
rem是根据根的font-size变化,而em是根据父级的font-size变化
rem:相对于根元素html的font-size
html {
font-size: 20px;
}
div {
font-size: 2rem;
}
//相当于
div {
font-size: 40px;
}
em:相对于父元素计算
p {
font-size: 20px;
}
// p为span父元素
p span {
font-size: 2rem;
}
//相当于
p span {
font-size: 40px; // 20*2=40
}
通配符选择器:* ID选择器:#myid 类选择器:.myclassname 元素选择器:div、span、p、a 等 相邻选择器(h1+p) 子选择器(ul < li) 后代选择器:p span、div a 等 伪类选择器:a:hover、 li:nth-child 等 属性选择器:a[rel="external"]、input[type="text"] [class*="col-"] 选择所有类名中含有"col-"的元素 [class^="col-"] 选择所有类名中以"col-"开头的元素 [class$="-col"] 选择所有类名中以"-col"结尾的元素
!important --> 行内样式 --> #id --> .class --> 元素和伪元素 --> * --> 继承 --> 默认
值 | 描述 |
---|---|
absolute | 生成绝对定位的元素,相对于static定位以外的第一个父元素(最近的已定位的祖先元素)进行定位。元素的位置通过left, right, top, bottom进行规定 |
fixed | 生成绝对定位的元素,相对于浏览器窗口进行定位。元素的位置通过left, right, top, bottom进行规定 |
relative | 生成相对定位的元素,相对于其正常位置定位。元素的位置通过left, right, top, bottom进行规定 |
static | 默认值,忽略 top, bottom, left, right和z-index |
inherit | 从父元素继承该属性的值 |
<style type="text/css">
@import url(CSS文件路径地址);
</style>
<link href="CSSurl路径" rel="stylesheet" type="text/css">
/*单行*/ .single { width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; word-break: break-all; } /*多行*/ .mutiple { display: -webkit-box; /*重点,不能用block等其他,将对象作为弹性伸缩盒子模型显示*/ -webkit-box-orient: vertical; /*从上到下垂直排列子元素(设置伸缩盒子的子元素排列方式)*/ -webkit-line-clamp: 3; /*行数,超出三行隐藏且多余的用省略号表示...*/ line-clamp: 3; overflow: hidden; max-width: 100%; } /*多行考虑兼容性*/ p.compatible { position: relative; line-height: 20px; max-height: 40px; overflow: hidden; } p.compatible::after { content: "..."; position: absolute; bottom: 0; right: 0; padding-left: 40px; background: -webkit-linear-gradient(left, transparent, #fff 55%); background: -o-linear-gradient(right, transparent, #fff 55%); background: -moz-linear-gradient(right, transparent, #fff 55%); background: linear-gradient(to right, transparent, #fff 55%); }
详情戳:https://blog.csdn.net/a1056244734/article/details/106772336
V8 编译 JS 代码的过程
Undefined
、Null
、Boolean
、Number
、String
、ES6新增:Symbol
Object是JavaScript中所有对象的父对象
数据封装对象:Object
、Array
、Boolean
、Number
和String
其他对象:Function
、Arguments
、Math
、Date
、RegExp
、Error
1、不要在同一行声明多个变量
2、请使用===/!==来比较true/false或者数值
3、使用对象字面量[1,2]替代new Array这种形式
4、不要使用全局变量
5、Switch语句必须带有default分支
6、函数不应该有时候有返回值,有时候没有返回值
7、for循环必须使用大括号
8、if语句必须使用大括号
9、for-in循环中的变量 应该使用var关键字明确限定作用域,从而避免作用域污染
行内引入
<body>
<input type="button" onclick="alert('行内引入')" value="按钮"/>
<button onclick="alert(123)">点击我</button>
</body>
内部引入
<!DOCTYPE html>
<html>
<head></head>
<body>
<script>
window.onload = function () {
alert("js 内部引入!")
}
</script>
</body>
</html>
外部引入
<body>
<div></div>
<script type="text/javascript" src="./js/index.js"></script>
</body>
注意
1,不推荐行内引入或者HTML中插入<script>,因为浏览器解析顺序缘故,如果解析到死循环之类的JS代码,会卡住页面
2,建议在onload事件之后,即等HTML、CSS渲染完毕再执行代码
// 合并a,b两个数组,两种方法
const a = [1,2,3]; const b = [4,5,6];
const c = a.concat(b)
a.push(...b) // a会被改变
详情请戳:https://blog.csdn.net/a1056244734/article/details/106941027
get请求类似于查找的过程,用户获取数据,可以不用每次都与数据库连接,所以可以使用缓存。
post不同,post做的一般是修改和删除的工作,所以必须与数据库交互,所以不能使用缓存。因此get请求适合于请求缓存。
cookies
、 sessionStorage
和localstorage
区别相同点:都用于客户端(浏览器)存储
不同点:
1. 存储大小:
- cookies
: 4K左右,
- sessionStorage
和localstorage
5M或更大
2. 数据与服务器之间的交互方式:
- cookie的数据会自动传递到服务器,服务端也可以写cookie到客户端
- sessionStorage
和localstorage
不会自动把数据发给服务器,仅在客户端本地保存
3. 有效时间:
- cookies
的有效期是服务端配置的一个过期时间,在此之前一直有效,即使窗口或浏览器关闭
- sessionStorage
数据在当前浏览器窗口关闭后自动删除
- localstorage
存储持久数据,浏览器关闭后数据不丢失除非主动删除数据
更多详情请戳:浏览器本地存储cookie、localStorage 与 sessionStorage
闭包
就是能够读取其他函数内部变量的函数。
在javascript中,只有函数(A)内部的子函数(B)才能读取局部变量(A中的),所以闭包可以理解成“定义在一个函数内部的函数”。在本质上,闭包是将函数内部和函数外部连接起来的桥梁。
闭包的特征
闭包用途
闭包的好处
能够实现封装和缓存等
闭包的坏处
消耗内存、不正当使用会造成内存溢出的问题
使用闭包的注意点
1)由于闭包会使得函数中的变量都被保存在内存中,内存消耗很大,所以不能滥用闭包,否则会造成网页的性能问题,在IE中可能导致内存泄露。
解决方法是,在退出函数之前,将不使用的局部变量全部删除。
2)闭包会在父函数外部,改变父函数内部变量的值。所以,如果你把父函数当作对象(object)使用,把闭包当作它的公用方法(Public Method),把内部变量当作它的私有属性(private value),这时一定要小心,不要随便改变父函数内部变量的值。
闭包大厂面试题
描述:下面代码能否实现点击某个按钮,body的背景色改为按钮对应的颜色,若不能,如何改进(腾讯)
<!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0"> <style> * { margin: 0; padding: 0; } html, body { height: 100%; overflow: hidden; } button { padding: 5px 10px; cursor: pointer; } </style> </head> <body> <!----> <button value="red">红</button> <button value="green">绿</button> <button value="blue">蓝</button> <script> var body = document.querySelector('body'), buttons = document.querySelectorAll('button'), arr = ['red', 'green', 'blue'] for (var i = 0; i < buttons.length; i++) { buttons[i].onclick = function () { body.style.background = arr[i] } } </script> </body> </html>
答案当然是不能,因为通过var
定义的变量,在for循环中的i
是全局的,变量提升、3次循环过后,i=3
,因为点击每个都相当于点击最后一个。
优化
for (var i = 0; i < buttons.length; i++) {
var item = buttons[i]
//=>在循环的时候,把每一个按钮的索引赋值给当前按钮(元素对象)的myIndex自定义属性
item.myIndex = i
item.onclick = function () {
// this => 当前点击的这个按钮
body.style.background = arr[this.myIndex]
}
}
// 都是利用闭包的机制去解决的
for (var i = 0; i < buttons.length; i++) {
buttons[i].onclick = (function (i) {
return function anonymous() {
body.style.background = arr[i]
}
})(i)
}
var
换为let
如果代码块中出现了 let/const/function 则当前代码块会产生一个 块级上下文(词法/块级作用域) => 私有的上下文
let的方法和闭包的方法原理类似,都是每一轮循环产生一个私有的作用域,(LET块级作用域),保存住当前循环i的值,以供后期调用。
var body = document.querySelector('body'),
buttons = document.querySelectorAll('button'),
arr = ['red', 'green', 'blue']
for (let i = 0; i < buttons.length; i++) {
// 私有的块级上下文
// 循环几次会产生几个块级上下文
buttons[i].onclick = function () {
body.style.background = arr[i]
}
}
后期真实项目中推荐大家使用自定义属性方法。不会产生那么多不销毁的私有作用域。
详情戳:闭包详解
作用域:在JavaScript中,作用域分为全局作用域
和函数作用域
全局作用域:代码在程序的任何地方都能被访问,window 对象的内置属性都拥有全局作用域
函数作用域:在固定的代码片段才能被访问
this 是和执行上下文绑定的。
执行上下文:
根据优先级最高的来决定 this
最终指向哪里。
() => {}
)new Person()
)bind/apply/call
)obj.foo()
)foo()
)几点寻找this规律:
this
是当前操作的元素(隐式绑定)this
就是谁(隐式绑定),没有点this
就是window
(在严格模式下("use strict
")没有点this
是undefined
,也成为默认绑定)this
是当前类的实例this
的指向(显式绑定)eg1:
var fullName = 'language'
var obj = {
fullName: 'javascript',
prop: {
getFullName: function () {
return this.fullName
}
}
}
console.log(obj.prop.getFullName())
var test = obj.prop.getFullName
console.log(test())
输出:javascript
language
此题符合第二条规律,方法执行时obj.prop.getFullName()
的getFullName()
前方有点,this
为obj.prop
,输出obj.prop.fullName
即javascript
;test()
前面没有点,this
为window,输出window.fullName
即language
;
详情戳:https://blog.csdn.net/a1056244734/article/details/107181337
全局作用域
对象在代码中的任何地方都能访问,其生命周期伴随着页面的生命周期。
函数作用域
函数内部定义的变量或者函数,并且定义的变量或者函数只能在函数内部被访问。函数执行结束之后,函数内部定义的变量会被销毁。
局部作用域
使用一对大括号包裹的一段代码,比如函数、判断语句、循环语句,甚至单独的一个{}都可以被看作是一个块级作用域。
例子:
作用域有上下级关系,上下级关系的确定就看函数是在哪个作用域下创建的。
如上,fn作用域下创建了bar函数,那么“fn作用域”就是“bar作用域”的上级。
作用域最大的用处就是隔离变量,不同作用域下同名变量不会有冲突。
变量取值:到创建 这个变量 的函数的作用域中取值
一般情况下,变量取值到 创建 这个变量 的函数的作用域中取值。
但是如果在当前作用域中没有查到值,就会向上级作用域去查,直到查到全局作用域,这么一个查找过程形成的链条就叫做作用域链。
var a = 0,
b = 0
function A(a) {
A = function (b) {
console.log(a + b++)
}
console.log(a++)
}
A(1)
A(2)
输出:1
4
图解:
注意点:函数找上级作用域时,有一个准则:它的亲爹妈是谁,上级作用域就是谁,即它在哪儿创建的,上级作用域就是谁。
更多作用域易错面试题请戳:JavaScript中函数作用域相关易错面试题
原型和原型链的概念
其实每个 JS 对象都有 __proto__
属性,这个属性指向了原型。
原型也是一个对象,并且这个对象中包含了很多函数,对于 obj
来说,可以通过 __proto__
找到一个原型对象,在该对象中定义了很多函数让我们来使用。
原型链:
Object
是所有对象的爸爸,所有对象都可以通过 __proto__
找到它Function
是所有函数的爸爸,所有函数都可以通过 __proto__
找到它prototype
是一个对象__proto__
属性指向原型,__proto__
将对象和原型连接起来组成了原型链原型和原型链的关系
instance.constructor.prototype = instance.__proto__
原型和原型链的特点
JavaScript对象是通过引用来传递的,我们创建的每个新对象实体中并没有一份属于自己的原型副本。当我们修改原型时,与之相关的对象也会继承这一改变
当我们需要一个属性的时,Javascript引擎会先看当前对象中是否有这个属性, 如果没有的就会查找他的Prototype对象是否有这个属性,如此递推下去,一直检索到 Object 内建对象
主要宏任务:整段脚本script
setTimeout
setInterval
Promise主体
其他宏任务:setImmediate
I/O
UI rendering
主要微任务:promise.then catch finally
process.nextTick(Node使用)
MutationObserver(浏览器使用)
MessageChannel
预加载:提前加载图片,当用户需要查看时可直接从本地缓存中渲染
懒加载:懒加载的主要目的是作为服务器前端的优化,减少请求数或延迟请求数
两种技术的本质:两者的行为是相反的,一个是提前加载,一个是迟缓甚至不加载。预加载则会增加服务器前端压力,懒加载对服务器有一定的缓解压力作用。
mouseover:当鼠标移入元素或其子元素都会触发事件,所以有一个重复触发,冒泡的过程。对应的移除事件是mouseout
mouseenter:当鼠标移入元素本身(不包含元素的子元素)会触发事件,也就是不会冒泡,对应的移除事件是mouseleave
promise、generator、async/await
instanceof
运算符用来检测 constructor.prototype
是否存在于参数 object
的原型链上。
typeof
操作符返回一个字符串,表示未经计算的操作数的类型。
var ClassFirst = function () {};
var ClassSecond = function () {};
var instance = new ClassFirst();
typeof instance; // 'object'
typeof instance == 'ClassFirst'; // false
instance instanceof Object; // true
instance instanceof ClassFirst; // true
instance instanceof ClassSecond; // false
typeof
对于原始类型来说,除了 null
都可以显示正确的类型typeof
对于对象来说,除了函数都会显示 'object'
如果变量可能未定义,使用typeof
比较好。
typeof undefinedVariable // "undefined"
undefinedVariable instanceof Object // throws an exception
如果变量可能为null
,使用instanceof
比较好。
var myNullVar = null;
typeof myNullVar; // "object"
myNullVar instanceof Object; // false
详细请戳:https://blog.csdn.net/a1056244734/article/details/107231545
输出:[1, NaN, NaN]
。
var new_array = arr.map(function callback(currentValue[, index[, array]]) { // Return element for new_array }[, thisArg])
这个callback一共可以接收三个参数,其中第一个参数代表当前被处理的元素,而第二个参数代表该元素的索引。
而parseInt则是用来解析字符串的,使字符串成为指定基数的整数。
parseInt(string, radix)
接收两个参数,第一个表示被处理的值(字符串),第二个表示为解析时的基数。
了解这两个函数后,我们可以模拟一下运行情况
[1, NaN, NaN]
触发高频事件后n秒内函数只会执行一次,如果n秒内高频事件再次被触发,则重新计算时间
思路:
每次触发事件时都取消之前的延时调用方法
function debounce(fn) {
let timeout = null; // 创建一个标记用来存放定时器的返回值
return function () {
clearTimeout(timeout); // 每当用户输入的时候把前一个 setTimeout clear 掉
timeout = setTimeout(() => { // 然后又创建一个新的 setTimeout, 这样就能保证输入字符后的 interval 间隔内如果还有字符输入的话,就不会执行 fn 函数
fn.apply(this, arguments);
}, 500);
};
}
function sayHi() {
console.log('防抖成功');
}
var inp = document.getElementById('inp');
inp.addEventListener('input', debounce(sayHi)); // 防抖
高频事件触发,但在n秒内只会执行一次,所以节流会稀释函数的执行频率
每次触发事件时都判断当前是否有等待执行的延时函数
function throttle(fn) { let canRun = true; // 通过闭包保存一个标记 return function () { if (!canRun) return; // 在函数开头判断标记是否为true,不为true则return canRun = false; // 立即设置为false setTimeout(() => { // 将外部传入的函数的执行放在setTimeout中 fn.apply(this, arguments); // 最后在setTimeout执行完毕后再把标记设置为true(关键)表示可以执行下一次循环了。当定时器没有执行的时候标记永远是false,在开头被return掉 canRun = true; }, 500); }; } function sayHi(e) { console.log(e.target.innerWidth, e.target.innerHeight); } window.addEventListener('resize', throttle(sayHi));
这题主要是考察这三者在事件循环中的区别,事件循环中分为宏任务队列和微任务队列。
async function async1() { console.log('async1 start'); await async2(); console.log('async1 end'); } async function async2() { console.log('async2'); } console.log('script start'); setTimeout(function() { console.log('setTimeout'); }, 0) async1(); new Promise(function(resolve) { console.log('promise1'); resolve(); }).then(function() { console.log('promise2'); }); console.log('script end');
输出:script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
详情请戳:JavaScript执行机制,任务队列、宏任务和微任务
https://juejin.im/post/5d2c814c6fb9a07ecd3d8e43
async/await 是 Generator + co 的语法糖
co函数的实现:
// const co = require('co')
function co (it) {
return new Promise((resolve, reject) => {
// 异步的迭代,只能用递归的方法
const next = (data) => {
const { value, done } = it.next(data)
if (done) { // 如果执行完毕,则完成
resolve(value)
} else {
Promise.resolve(value).then(next, reject)
}
}
next()
})
}
async function async1() { console.log('async1 start') await async2() console.log('async1 end') } async function async2() { console.log('async2') } console.log('script start') setTimeout(function () { console.log('setTimeout') }, 0) async1() new Promise(function (resolve) { console.log('promise1') resolve() }).then(function () { console.log('promise2') }) console.log('script end')
结果:script start
async1 start
async2
promise1
script end
async1 end
promise2
setTimeout
考察重点:
整段脚本script
setTimeout
setInterval
、I/O
UI交互事件
postMessage
MessageChannel
setImmediate(Node.js 环境)
promise.then catch finally
process.nextTick(Node使用)
MutationObserver(浏览器使用)
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
等价于
async function async1() {
console.log('async1 start');
Promise.resolve(async2()).then(() => {
console.log('async1 end');
})
}
详情戳:几道面试题理清JavaScript执行机制(Event Loop),任务队列、宏任务和微任务
看过 Event Loop 基础原理的就明白,Promise构造函数是同步执行,而 .then .catch .啥啥的是异步(还有process.nextTick等等,大家可以查),
而且放到了微队列中,async/await 中,await 前面的是同步,await 后面的是异步,写法上是这样,但是其实是 语法糖,最后还会转为 Promise.then的形式
JS 异步已经告一段落了,这里来一波小总结
setTimeout(() => {
// callback 函数体
}, 1000)
缺点:回调地狱,不能用 try catch 捕获错误,不能 return
回调地狱的根本问题在于:
ajax('XXX1', () => {
// callback 函数体
ajax('XXX2', () => {
// callback 函数体
ajax('XXX3', () => {
// callback 函数体
})
})
})
优点:解决了同步的问题(只要有一个任务耗时很长,后面的任务都必须排队等着,会拖延整个程序的执行。)
Promise就是为了解决callback的问题而产生的。
Promise 实现了链式调用,也就是说每次 then 后返回的都是一个全新 Promise,如果我们在 then 中 return ,return 的结果会被 Promise.resolve() 包装
优点:解决了回调地狱的问题
ajax('XXX1')
.then(res => {
// 操作逻辑
return ajax('XXX2')
}).then(res => {
// 操作逻辑
return ajax('XXX3')
}).then(res => {
// 操作逻辑
})
缺点:无法取消 Promise ,错误需要通过回调函数来捕获
特点:可以控制函数的执行,可以配合 co 函数库使用
function *fetch() {
yield ajax('XXX1', () => {})
yield ajax('XXX2', () => {})
yield ajax('XXX3', () => {})
}
let it = fetch()
let result1 = it.next()
let result2 = it.next()
let result3 = it.next()
async、await 是异步的终极解决方案
优点是:代码清晰,不用像 Promise 写一大堆 then 链,处理了回调地狱的问题
缺点:await 将异步代码改造成同步代码,如果多个异步操作没有依赖性而使用 await 会导致性能上的降低。
async function test() {
// 以下代码没有依赖性的话,完全可以使用 Promise.all 的方式
// 如果有依赖性的话,其实就是解决回调地狱的例子了
await fetch('XXX1')
await fetch('XXX2')
await fetch('XXX3')
}
下面来看一个使用 await 的例子:
let a = 0
let b = async () => {
a = a + await 10
console.log('2', a) // -> '2' 10
}
b()
a++
console.log('1', a) // -> '1' 1
结果:'1' 1
'2' 10
对于以上代码你可能会有疑惑,让我来解释下原因
await 10
之前变量 a 还是 0,因为 await
内部实现了 generator
,generator 会保留堆栈中东西,所以这时候 a = 0 被保存了下来await
是异步操作,后来的表达式不返回 Promise 的话,就会包装成 Promise.reslove(返回值)
,然后会去执行函数外的同步代码a = 0 + 10
上述解释中提到了 await 内部实现了 generator,其实 await 就是 generator 加上 Promise的语法糖,且内部实现了自动执行 generator。如果你熟悉 co 的话,其实自己就可以实现这样的语法糖。
function _new(fn, ...arg) {
const obj = Object.create(fn.prototype);
const ret = fn.apply(obj, arg);
return ret instanceof Object ? ret : obj;
}
Vue 实例从创建到销毁的过程,就是生命周期。也就是从开始创建、初始化数据、编译模板、挂载Dom→渲染、更新→渲染、卸载等一系列过程,我们称这是 Vue 的生命周期
它的生命周期中有多个事件钩子,让我们在控制整个Vue实例的过程时更容易形成好的逻辑
它可以总共分为8个阶段:创建前/后, 载入前/后,更新前/后,销毁前/销毁后
第一次页面加载时会触发 beforeCreate, created, beforeMount, mounted 这几个钩子
DOM 渲染在 mounted 中就已经完成了
简化版本如下:
生命周期钩子的一些使用方法:
v-show是css切换,类似于display: none;
display: block;
切换。
v-if是完整的销毁和重新创建
使用 频繁切换时用v-show,运行时较少改变时用v-if
v-if=‘false’ v-if是条件渲染,当false的时候不会渲染
v-model :一般用在表达输入,很轻松的实现表单控件和数据的双向绑定
v-html: 更新元素的 innerHTML
v-show 与 v-if: 条件渲染, 注意二者区别
使用了v-if的时候,如果值为false,那么页面将不会有这个html标签生成
v-show则是不管值为true还是false,html元素都会存在,只是CSS中的display显示或隐藏
v-on : click: 可以简写为@click,@绑定一个事件。如果事件触发了,就可以指定事件的处理函数
v-for:基于源数据多次渲染元素或模板块
v-bind: 当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM
语法:v-bind:title="msg" 简写::title="msg"
//对象方法
v-bind:class="{'orange': isRipe, 'green': isNotRipe}"
// 数组方法
v-bind:class="[class1, class2]"
// 行内
v-bind:style="{color: color, fontSize: fontSize+'px' }"
1,<router-link to='/home/' + item.name>{{item.name}}</router-link> router-link标签会渲染为<a>标签,咋填template中的跳转都是这种;
2,另一种是编程是导航 也就是通过js跳转 比如 router.push('/home')
Vue全家桶:Vue + Vue-router + Vuex+axios
Vue-router:路由
Vuex:状态管理
axios:网络请求
Vue.js是一套构建用户界面的MVVM框架,model-view-viewModel,他的设计思想就是关注Model的变化,通过viewModel自动去更新DOM的状态,也就是Vue的一大特点:数据驱动。
详情请戳:https://blog.csdn.net/a1056244734/article/details/106938104
Vuex是什么?
VueX 是一个专门为 Vue.js 应用设计的状态管理架构,统一管理和维护各个vue组件的可变化状态(你可以理解成 vue 组件里的某些 data )。
vuex 就是一个仓库,仓库里放了很多对象。其中 state 就是数据源存放地,对应于一般 vue 对象里面的 data
state 里面存放的数据是响应式的,vue 组件从 store 读取数据,若是 store 中的数据发生改变,依赖这相数据的组件也会发生更新
它通过 mapState 把全局的 state 和 getters 映射到当前组件的 computed 计算属性
Vue有五个核心概念,state, getters, mutations, actions, modules。
总结
state => Vuex 使用单一状态树,即每个应用将仅仅包含一个store 实例,但单一状态树和模块化并不冲突。存放的数据状态,不可以直接修改里面的数据
getters => 从基本数据派生的数据
mutations => 提交更改数据的方法,同步!
actions => 像一个装饰器,包裹mutations,使之可以异步。
modules => 模块化Vuex
总结
vuex 一般用于中大型 web 单页应用中对应用的状态进行管理,对于一些组件间关系较为简单的小型应用,使用 vuex 的必要性不是很大,因为完全可以用组件 prop 属性或者事件来完成父子组件之间的通信,vuex 更多地用于解决跨组件通信以及作为数据中心集中式存储数据
vue数据双向绑定是通过数据劫持结合发布者-订阅者模式的方式来实现的。
vue 项目中主要使用 v-model 指令在表单 input、textarea、select 等元素上创建双向数据绑定,我们知道 v-model 本质上不过是语法糖,v-model 在内部为不同的输入元素使用不同的属性并抛出不同的事件:
以 input 表单元素为例:
<input v-model='name'>
相当于
<input v-bind:value="name" v-on:input="name = $event.target.value">
如果在自定义组件中,v-model 默认会利用名为 value 的 prop 和名为 input 的事件,如下所示:
父组件:
<ModelChild v-model="message"></ModelChild>
子组件:
<div>{{value}}</div>
props:{
value: String
},
methods: {
test1(){
this.$emit('input', '小红')
},
},
Vue 请求数据方式有:vue-resource
、axios
、fetchJsonp
三种。其中,vue-resource
是 Vue
官方提供的插件,axios
与 fetchJsonp
是第三方插件。
一、vue-resource 请求数据,目前已废弃
npm 安装 vue-resource
npm install vue-resource --save
说明:使用 --save
是为了在 package.json
中引用,表示在生产环境中使用。如果要将代码打包发送他人、上传到github,或者要发布代码时,package.json
就是安装所需要的包。那么,当在开发环境中时,使用 --save-dev
;当在生产环境中,使用 --save
。
在 main.js 即入口文件中引入 vue-resource;
import VueResource from 'vue-resource';
引入后,需使用;
Vue.use(VueResource);
在代码中使用:
{
// GET /someUrl
this.$http.get('/someUrl').then(response => {
// get body data
this.someData = response.body;
}, response => {
// error callback
});
}
注意:vue-resource
的请求都是继承promise
的。promise
是属于异步请求;.then
箭头函数里的this代表的是上下文。如果想获取外面的数据,请在在函数外声明,var that = this
;将外层的this先储存到that中再引入函数内部。
vue-resource 参考文档:https://github.com/pagekit/vue-resource/blob/develop/docs/http.md
二、 axios 请求数据
axios 参考文档:https://github.com/axios/axios
三、fetchJsonp 请求数据
参考文档: https://github.com/camsong/fetch-jsonp
computed:
1. computed是计算属性,也就是计算值,它更多用于计算值的场景
2. computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
3. computed适用于计算比较消耗性能的计算场景
watch:
1. 更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
2. 无缓存性,页面重新渲染时值不变化也会执行
小结:
1. 当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
2. 如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以 更准确、更快速
准确:
如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug
快速:
利用key的唯一性生成map对象来获取对应节点,比遍历方式更快。
为什么组件中的data必须是一个函数,然后return一个对象,而new Vue实例里,data可以直接是一个对象?
// data
data() {
return {
message: "子组件",
childName:this.name
}
}
// new Vue
new Vue({
el: '#app',
router,
template: '<App/>',
components: {App}
})
因为组件是用来复用的,JS里对象是引用关系,这样作用域没有隔离,而new Vue的实例,是不会被复用的,因此不存在引用对象问题。
Class 可以通过对象语法和数组语法进行动态绑定:
对象语法
<div v-bind:class="{ active: isActive, 'disable': disabled }"></div>
data: {
isActive: true,
disabled: false
}
数组语法
<div v-bind:class="[isActive ? activeClass : '', errorClass]"></div>
data: {
activeClass: 'active',
errorClass: 'text-danger'
}
Style 也可以通过对象语法和数组语法进行动态绑定:
对象语法
<div v-bind:style="{ color: activeColor, fontSize: fontSize + 'px' }"></div>
data: {
activeColor: 'red',
fontSize: 30
}
数组语法
<div v-bind:style="[styleColor, styleSize]"></div>
data: {
styleColor: {
color: 'red'
},
styleSize:{
fontSize:'23px'
}
}
所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解
额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。子组件想修改时,只能通过 $emit 派发一个自定义事件,父组件接收到后,由父组件修改
有两种常见的试图改变一个 prop 的情形 :
在这种情况下,最好定义一个本地的 data 属性并将这个 prop 用作其初始值:
props: ['initialCounter'],
data: function () {
return {
counter: this.initialCounter
}
}
在这种情况下,最好使用这个 prop 的值来定义一个计算属性
props: ['size'],
computed: {
normalizedSize: function () {
return this.size.trim().toLowerCase()
}
}
keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,用法也很简单:
<keep-alive>
<component>
<!-- 该组件将被缓存! -->
</component>
</keep-alive>
props
注意:其中 exclude 的优先级比 include 高
// 组件 a
export default {
name: 'a',
data () {
return {}
}
}
<keep-alive include="a">
<component>
<!-- name 为 a 的组件将被缓存! -->
</component>
</keep-alive>可以保留它的状态或避免重新渲染
<keep-alive exclude="a">
<component>
<!-- 除了 name 为 a 的组件都将被缓存! -->
</component>
</keep-alive>可以保留它的状态或避免重新渲染
钩子函数
在下次DOM更新循环结束之后执行延迟回调。在修改数据之后,立即使用的这个回调函数,获取更新后的DOM。
一、示例
先来一个示例了解下关于Vue中的DOM更新以及nextTick的作用。
模板
<div class="app">
<div ref="msgDiv">{{msg}}</div>
<div v-if="msg1">Message got outside $nextTick: {{msg1}}</div>
<div v-if="msg2">Message got inside $nextTick: {{msg2}}</div>
<div v-if="msg3">Message got outside $nextTick: {{msg3}}</div>
<button @click="changeMsg">
Change the Message
</button>
</div>
Vue实例
new Vue({ el: '.app', data: { msg: 'Hello Vue.', msg1: '', msg2: '', msg3: '' }, methods: { changeMsg() { this.msg = "Hello world." this.msg1 = this.$refs.msgDiv.innerHTML this.$nextTick(() => { this.msg2 = this.$refs.msgDiv.innerHTML }) this.msg3 = this.$refs.msgDiv.innerHTML } } })
点击前
点击后
从图中可以得知:msg1和msg3显示的内容还是变换之前的,而msg2显示的内容是变换之后的。其根本原因是因为Vue中DOM更新是异步的。
二、应用场景
下面了解下nextTick的
主要应用的场景及原因。
created()
钩子函数进行的DOM操作一定要放在Vue.nextTick()
的回调函数中在created()
钩子函数执行的时候DOM 其实并未进行任何渲染,而此时进行DOM操作无异于徒劳,所以此处一定要将DOM操作的js代码放进Vue.nextTick()
的回调函数中。与之对应的就是mounted()
钩子函数,因为该钩子函数执行时所有的DOM挂载和渲染都已完成,此时在该钩子函数中进行任何DOM操作都不会有问题 。
具体原因在Vue的官方文档中详细解释:
Vue 异步执行 DOM 更新。只要观察到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据改变。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作上非常重要。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部尝试对异步队列使用原生的 Promise.then 和MessageChannel,如果执行环境不支持,会采用 setTimeout(fn, 0)代替。
例如,当你设置vm.someData = ‘new value’,该组件不会立即重新渲染。当刷新队列时,组件会在事件循环队列清空时的下一个“tick”更新。多数情况我们不需要关心这个过程,但是如果你想在 DOM 状态更新后做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员沿着“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们确实要这么做。为了在数据变化之后等待 Vue 完成更新 DOM ,可以在数据变化之后立即使用Vue.nextTick(callback) 。这样回调函数在 DOM 更新完成后就会调用。
详细请戳:Vue插槽详解
单个插槽
当子组件模板只有一个没有属性的插槽时,
父组件传入的整个内容片段将插入到插槽所在的 DOM 位置,
并替换掉插槽标签本身
命名插槽
solt元素可以用一个特殊的特性name来进一步配置如何分发内容。
多个插槽可以有不同的名字。 这样可以将父组件模板中 slot 位置,
和子组件 slot 元素产生关联,便于插槽内容对应传递
作用域插槽
可以访问组件内部数据的可复用插槽(reusable slot)
在父级中,具有特殊特性 slot-scope 的<template> 元素必须存在,
表示它是作用域插槽的模板。slot-scope 的值将被用作一个临时变量名,
此变量接收从子组件传递过来的 prop 对象
浅拷贝是拷贝了对象的引用,当原对象发生变化的时候,拷贝对象也跟着变化;
深拷贝是另外申请了一块内存,内容和原对象一样,更改原对象,拷贝对象不会发生变化。
浅拷贝:有两种方式,一种是把一个对象里面的所有的属性值和方法都复制给另一个对象,另一种是直接把一个对象赋给另一个对象,使得两个都指向同一个对象。
深拷贝:把一个对象的属性和方法一个个找出来,在另一个对象中开辟对应的空间,一个个存储到另一个对象中。
两者就在于,浅拷贝只是简单的复制,对对象里面的对象属性和数组属性只是复制了地址,并没有创建新的相同对象或者数组。而深拷贝是完完全全的复制一份,空间大小占用一样但是位置不同!!
let
和const
不存在变量提升机制var/function
有变量提升,而let/const/class/import
都不存在这个机制var
允许重复声明,而let
不允许重复声明其中let和const的区别
ES6 允许按照一定模式,从数组和对象中提取值,对变量进行赋值,这被称为解构(Destructuring)。
let arr = ['this is a string', 2, 3];
//传统方式
let a = arr[0],
b = arr[1],
c = arr[2];
//解构赋值,是不是简洁很多?
let [a, b, c] = arr
console.log(a);//this is a string
console.log(b);//2
console.log(c);//3
解构不仅可以用于数组,还可以用于对象。
let { foo, bar } = { foo: 'aaa', bar: 'bbb' }
foo // "aaa"
bar // "bbb"
let obj = {
name: 'chris',
sex: 'male',
age: 26,
son: {
sonname: '大熊',
sonsex: 'male',
sonage: 1
}
}
const {name, sex, age, son} = obj
console.log(name + ' ' + sex + ' ' + age) //chris male 26
console.log(son) // { sonname: '大熊', sonsex: 'male', sonage: 1 }
ES6 的有些数据结构原生具备 Iterator 接口(比如数组),即不用任何处理,就可以被for…of循环遍历。原因在于,这些数据结构原生部署了Symbol.iterator属性,另外一些数据结构没有(比如对象)。凡是部署了Symbol.iterator属性的数据结构,就称为部署了遍历器接口。调用这个接口,就会返回一个遍历器对象。
对数组和 Set 结构进行解构赋值时,会默认调用Symbol.iterator方法。
https://wangdoc.com/es6/iterator.html
对象解构:
对象的解构赋值是下面形式的简写(参见《对象的扩展》一章)。
let { foo: foo, bar: bar } = { foo: 'aaa', bar: 'bbb' };
也就是说,对象的解构赋值的内部机制,是先找到同名属性,然后再赋给对应的变量。真正被赋值的是后者,而不是前者。
let { foo: baz } = { foo: 'aaa', bar: 'bbb' };
baz // "aaa"
foo // error: foo is not defined
上面代码中,foo是匹配的模式,baz才是变量。真正被赋值的是变量baz,而不是模式foo。
https://es6.ruanyifeng.com/#docs/destructuring
扩展运算符(spread运算符)用三个点号(…)表示,功能是把数组或类数组对象展开成一系列用逗号隔开的值。
let foo = function(a, b, c) { console.log(a); console.log(b); console.log(c); } let arr = [1, 2, 3]; //传统写法 foo(arr[0], arr[1], arr[2]); //使用扩展运算符 foo(...arr); //1 //2 //3
rest运算符也是三个点号,不过其功能与扩展运算符恰好相反,把逗号隔开的值序列组合成一个数组。
//主要用于不定参数,所以ES6开始可以不再使用arguments对象 var bar = function(...args) { for (let el of args) { console.log(el); } } bar(1, 2, 3, 4); //1 //2 //3 //4 bar = function(a, ...args) { console.log(a); console.log(args); } bar(1, 2, 3, 4); //1 //[ 2, 3, 4 ]
详情请戳:ES6中解构、扩展运算符和rest运算符
应用场景Set用于数据重组,Map用于数据储存
Set:
weakSet:
Map:
weakMap:
Promise 是异步编程的一种解决方案,比传统的解决方案——回调函数和事件——更合理和更强大。
const promise = new Promise(function(resolve, reject) {
if (/* 异步操作成功 */) {
resolve(value)
} else {
reject(error)
}
})
promise.then(function (value) {
// success
}, function (error) {
/// failure
})
class Super {}
class Sub extends Super {}
const sub = new Sub();
Sub.__proto__ === Super;
子类可以直接通过 proto 寻址到父类。
function Super() {}
function Sub() {}
Sub.prototype = new Super();
Sub.prototype.constructor = Sub;
var sub = new Sub();
Sub.__proto__ === Function.prototype;
而通过 ES5 的方式,Sub.__proto__ === Function.prototype
更多戳:git基本指令使用,git status,git stash,git merge
WebPack可以看做是模块打包机:它做的事情是,分析你的项目结构,找到JavaScript模块以及其它的一些浏览器不能直接运行的拓展语言(Less、Sass、TypeScript
等),并将其转换和打包为合适的格式供浏览器使用。
当 Webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。
Webpack的工作方式是:把你的项目当做一个整体,通过一个给定的主文件(如:index.js),Webpack将从这个文件开始找到你的项目的所有依赖文件,使用loaders处理它们,最后打包为一个(或多个)浏览器可识别的JavaScript文件。
happypack
,实现webpack多线程打包,显著提高本地打包速度。webpack DllReferencePlugin
,提前打包公共代码(polyfill和vue全家桶),提高构建速度。webpack devServer
中的express插槽,prod环境使用express static
映射前端静态文件。webpack的缺点是只能用于采用模块化开发的项目
目前网络分层可分为两种:OSI 模型和 TCP/IP 模型
OSI模型
应用层(Application)
表示层(Presentation)
会话层(Session)
传输层(Transport)
网络层(Network)
数据链路层(Data Link)
物理层(Physical)
TCP/IP模型
应用层(Application)
传输层(Host-to-Host Transport)
互联网层(Internet)
网络接口层(Network Interface)
1、https协议需要到ca申请证书,一般免费证书较少,因而需要一定费用
2、http是超文本传输协议,信息是明文传输,https则是具有安全性的ssl加密传输协议
3、http和https使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443
4、http的连接很简单,是无状态的;HTTPS协议是由SSL+HTTP协议构建的可进行加密传输、身份认证的网络协议,比http协议安全。
状态码 | 类别 | 描述 |
---|---|---|
1xx | Informational(信息状态码) | 接受请求正在处理 |
2xx | Success(成功状态码) | 请求正常处理完毕 |
3xx | Redirection(重定向状态码) | 需要附加操作已完成请求 |
4xx | Client Error(客户端错误状态码) | 服务器无法处理请求 |
5xx | Server Error(服务器错误状态码) | 服务器处理请求出错 |
状态码 | 状态码英文名称 | 中文描述 |
---|---|---|
200 | OK | 请求成功 。一般用于GET与POST请求 |
204 | No Content | 无内容。服务器成功处理,但未返回内容 。在未更新网页的情况下,可确保浏览器继续显示当前文档 |
301 | Moved Permanently | 永久性重定向 。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替 |
302 | Found | 临时性重定向 。与301类似。但资源只是临时被移动。客户端应继续使用原有URI |
303 | See Other | 查看其它地址 。与302类似。使用GET请求查看 |
304 | Not Modified | 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源 |
400 | Bad Request | 客户端请求报文中存在语法错误,服务器无法理解 。浏览器会像200 OK一样对待该状态吗 |
401 | Unauthorized | 请求要求用户的身份认证 ,通过HTTP认证(BASIC认证,DIGEST认证)的认证信息,若之前已进行过一次请求,则表示用户认证失败 |
403 | Forbidden | 服务器理解请求客户端的请求,但是拒绝执行此请求 |
404 | Not Found | 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面。也可以在服务器拒绝请求且不想说明理由时使用 |
500 | Internal Server Error | 服务器内部错误,无法完成请求 ,也可能是web应用存在bug或某些临时故障 |
503 | Service Unavailable | 由于超载或系统维护,服务器暂时的无法处理客户端的请求 。延时的长度可包含在服务器的Retry-After头信息中 |
误区:我们经常说get请求参数的大小存在限制,而post请求的参数大小是无限制的
实际上HTTP 协议从未规定 GET/POST 的请求长度限制是多少。对get请求参数的限制是来源与浏览器或web服务器,浏览器或web服务器限制了url的长度。为了明确这个概念,我们必须再次强调下面几点:
为什么要用axios?
axios 是一个基于Promise 用于浏览器和 nodejs 的 HTTP 客户端,它本身具有以下特征:
1. axios:
2. jQuery ajax:
在 HTTP/1 中,每次请求都会建立一次HTTP连接,也就是我们常说的3次握手4次挥手,这个过程在一次请求过程中占用了相当长的时间,即使开启了 Keep-Alive ,解决了多次连接的问题,但是依然有两个效率上的问题:
(1)优化图片格式和大小;
(2)服务端开启gzip;
(3)使用浏览器缓存;
(4)减少重定向请求;
(5)使用CDN存储静态资源;
(6)减少DNS查询次数;
(7)压缩css和js内容;
(9) 后端语法优化;
(10)换个好点的服务器
1、避免 HTML 中书写 CSS 代码,因为这样难以维护。
2、使用 Viewport 加速页面的渲染。
3、使用语义化标签,减少 CSS 代码,增加可读性和 SEO。
4、减少标签的使用,DOM 解析是一个大量遍历的过程,减少不必要的标签,能降低遍历的次数。
5、避免 src、href 等的值为空,因为即时它们为空,浏览器也会发起 HTTP 请求。
6、减少 DNS 查询的次数
1、优化选择器路径:使用 .c {} 而不是 .a .b .c {}。
2、选择器合并:共同的属性内容提起出来,压缩空间和资源开销。
3、精准样式:使用 padding-left: 10px 而不是 padding: 0 0 0 10px。
4、雪碧图:将小的图标合并到一张图中,这样所有的图片只需要请求一次。
5、避免通配符:.a .b * {} 这样的选择器,根据从右到左的解析顺序在解析过程中遇到通配符 * {} 会遍历整个 DOM,性能大大损耗。
6、少用 float:float 在渲染时计算量比较大,可以使用 flex 布局。
7、为 0 值去单位:增加兼容性。
8、压缩文件大小,减少资源下载负担。
1、尽可能把 <script> 标签放在 body 之后,避免 JS 的执行卡住 DOM 的渲染,最大程度保证页面尽快地展示出来
2、尽可能合并 JS 代码:提取公共方法,进行面向对象设计等……
3、CSS 能做的事情,尽量不用 JS 来做,毕竟 JS 的解析执行比较粗暴,而 CSS 效率更高。
4、尽可能逐条操作 DOM,并预定好 CSS 样式,从而减少 reflow 或者 repaint 的次数。
5、尽可能少地创建 DOM,而是在 HTML 和 CSS 中使用 display: none 来隐藏,按需显示。
6、压缩文件大小,减少资源下载负担。
Performance
:排查应用性能的最佳工具。Audits
:对页面性能进行检测,根据测试结果进行优化。Yslow
。已知如下数组:
var arr = [ [1, 2, 2], [3, 4, 5, 5], [6, 7, 8, 9, [11, 12, [12, 13, [14] ] ] ], 10];
公司:携程
解法一:
const result = Array.from(new Set(arr1.flat(Infinity))).sort((a, b) => {
return a - b
})
console.log(result)
// [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]
知识点:
Array.prototype.flat()
用于将嵌套的数组“拉平”,变成一维的数组。该方法返回一个新数组,对原数据没有影响。[1, 2, [3, 4]].flat()
// [1, 2, 3, 4]
https://es6.ruanyifeng.com/#docs/array#数组实例的-flat,flatMap
new Set(arr)
可用于数组去重function flatten(arr) {
while (arr.some(item => Array.isArray(item))) {
arr = [].concat(...arr);
}
return arr;
}
Array.from(new Set(flatten(arr))).sort((a, b) => a - b)
解法3:
Array.from(new Set(arr.toString().split(","))).sort((a,b)=>{ return a-b}).map(Number)
在资源不足的设备上,将服务合并到浏览器进程中
1、自我介绍
2、你的项目中技术难点是什么?遇到了什么问题?你是怎么解决的?
3、你认为哪个项目做得最好?
4、平时是如何学习前端开发的?
5、你最有成就感的一件事?
6、你是怎么学习前端的?
1、面试完你还有什么问题要问的吗?
2、你有什么爱好?
3、你最大的优点和缺点是什么?
4、你为什么会选择这个行业,职位?
5、你觉得你适合从事这个岗位吗?
6、你有什么职业规划?
7、你对工资有什么要求?
8、如何看待前端开发?
9、未来三到五年的规划是怎样的?
高复用低耦合,这样文件小,好维护,而且好扩展
前端是最贴近用户的程序员,比后端、数据库、产品经理、运营、安全都近
前端是最贴近用户的程序员,前端的能力就是能让产品从 90分进化到 100 分,甚至更好,与团队成员,UI设计,产品经理的沟通;做好的页面结构,页面重构和用户体验;
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。