a
,b
,button
,i
,span
,img
,input
,textarea
,select
…
article
,dic
,form
,h1-h6
,header
,hr
,ul
,li
,p
,table
…
relative
, static
, absolute
, sticky
number
, boolean
, string
, undefined
, null
, object
, symbol
defer
: 表示script文件可以先下载,但是延迟执行(顺序执行),但是会早于DOMContentLoaded
执行。晚于页面解析渲染,早于全部结束。
async
: 告诉外部的script文件下载后,直接执行,不会根据顺序执行,所以script文件间出现相互依赖会出现问题。
#id
: id选择器
.class
: class选择器
tag
: 标签选择器
:link
: 伪类选择器
Cache-Control: max-age
> Expires
Etag / If-None-Match
> Last-Modified / If-Modified-Since
同步任务 > 微任务(Promise.then
, MutaionObserver
) > 宏任务(setInterval
、setTimeout
)
1xx 信息
2xx 成功
301 重定向
302 临时重定向
303 未修改
404 资源不存在
403 拒绝服务
408 请求超时
500 服务器错误
502 代理服务器错误
504 网关超时
function toSingeItem(arr){
return Array.from(new Set(arr))
}
// 第一种 递归
let res = [];
function flat(arr) {
arr.forEach(item => {
if (item instanceof Array) {
flat(item)
} else {
res.push(item)
}
})
}
console.log(res);
// 第二种 方法
arr.toString().split(',').map(item=>{
return Number(item)
})
function add(num1, num2) {
if (num1.length < num2.length) {
[num1, num2] = [num2, num1]
}
num1 = num1.split('').reverse().join('');
num2 = num2.split('').reverse().join('');
let res = '';
let f = 0;
for (let i = 0; i < num1.length; i++) {
let a = Number(num1[i]);
let b = 0;
if (num2.length > i) {
b = Number(num2[i])
}
res += (parseInt((a + b + f) % 10)).toString();
f = (parseInt((a + b) / 10))
}
if (f != 0 ) {
res += f.toString()
}
return res.split('').reverse().join('');
}
> 相乘同理
[0] == 0 //true
[ ] == 0 //true
'' == 0 //true
'' === [] //true
1 == 2 == 3 //false
1 == 2 == 0 //true
...
性能优化
1.padding撑开
.box{
width: 50%;
height: 0px;
padding-bottom: 50%;
}
2. 新方法的属性
.box{
width:50%;
height:50vw;
// 1vw = 1% viewport width; 1vh = 1% viewport height
}
3. 伪元素
.box{
width:50%;
overflow:hidden;
}
.box:before{
content:'';
display:block;
margin-top:100%;
}
1.绝对定位
.outer{
position: relative;
}
.inner{
left: 50%;
top: 50%;
transform: transate(-50%,-50%)
}
2. flex布局
.outer{
display: flex;
align-items: center;
justify-content: center;
}
// 延迟执行
let debounce = function(fnc, wait){
let timer = null;
return function(){
if(timer){
clearTimeout(timer)
}
timer = setTimeout(fnc, wait)
}
}
// 立即执行
let debounce = function(fnc, wait){
let timer = null;
return function(){
if(timer){
clearTimeout(timer)
}
if(!timer){
fn();
}
timer = setTimeout(()=>{
timer = null;
}, wait)
}
}
let thorrlte = function(fnc, wait){
let timer = new Date();
return function(){
let now =new Date();
if(now - timer > wait){
fnc();
timer = now;
}
}
}
//样例数据
let elementObj = ({
tagName: 'ul',
props: {'class': 'list'},
children: [
({tagName: 'li', children: ['douyin']}),
({tagName: 'li', children: ['toutiao']})
]
});
function createNode(elementObj){
let dom = document.createElement(elementObj.tagName);
Object.keys(elementObj.props).forEach(key=>{
dom.setAttribute(key, elementObj.props[key]);
});
if(elementObj.content){
dom.innerText = elementObj.content;
}
if(elementObj.children.length != 0){
elementObj.children.forEach(child => {
dom.appendChild(createNode(child));
})
}
return dom
}
<meta name="viewport" content="width=device-width, height=device-height, initial-scale=1, maximum-scale=1, user-scalable=no">
cookies:4K
localStorage:5M
sessionStorage:5M
cookies: 设置,超过即失效。
localStorage: 永久有效,除非手动删除。
sessionStorage: 在当前会话下有效,关闭页面或者浏览器时会被清空。
cookies:参与服务器端通信,每次都会携带http的头信息中。
localStorage不参与服务器端的通信。
sessionStorage不参与服务器端的通信。
// 父类 Person
let Person = function (name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
// 父类方法
Person.prototype.getName = function () {
return this.name;
}
let nys = new Person('nys', 'male', '23')
// 子类 学生
let Student = function (stu_id) {
this.stu_id = stu_id;
}
// 继承
Student.prototype = nys;
let stu = new Student('xh123')
// 优点
1. 简单
2. 基于原型链,从上到下关系一样
// 缺点
1. 来自原型对象的所有属性被所有实例共享
2. 无法实现多继承
3. 创建子类实例时,无法向父类构造函数传参
// 父类 Person
let Person = function (name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
// 子类 Student
let Student = function(name, sex, age, stu_id){
this.stu_id = stu_id;
Person.call(this, name, sex, age);
}
let stu = new Student('nys', 'male', 25, 'xh22150546');
console.log(stu instanceof Person)
// 优点:
1. 创建子类实例时,可以向父类传递参数
2. 可以实现多继承(call多个父类对象)
// 缺点:
1. 实例并不是父类的实例,只是子类的实例
2. 只能继承父类的实例属性和方法,不能继承原型属性/方法
3. 无法实现函数复用,每个子类都有父类实例函数的副本,影响性能
// 父类 Person
let Person = function (name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
// 子类 Student
let Student = function(name, sex, age, stu_id){
let p = new Person(name, sex, age);
p.stu_id = stu_id;
return p;
}
let stu = new Student('nys', 'male', 25, 'xh22150546');
// 优点:
1. 简单
2. 反正就是简单
// 缺点:
1. 不能多继承
2. 实例是父类的实例,不是子类自己的
// 父类 Person
let Person = function (name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
// 子类 Student
let Student = function(name, sex, age, stu_id){
let p = new Person(name, sex, age);
for(let key of p){
Student.prototype[key] = p[key]
}
Student.prototype['stu_id'] = stu_id
}
let stu = new Student('nys', 'male', 25, 'xh22150546');
// 优点:
1. 简单,就是把父类有的复制过来
2. 可以实现多继承
// 缺点:
1. 不能访问父类到不可以枚举方法
2. 如果父类过大,效率过低
3. 付费新增方法无法获取
class Person {
constructor(name, sex, age) {
this.name = name;
this.sex = sex;
this.age = age;
}
getName(){
return this.name;
}
}
class Student extend Person {
constructor (name, sex, age, stu_id){
super( name, sex, age );
this.stu_id = stu_id;
}
getID(){
return this.stu_id;
}
}
function instanceof(sub, sup){
let sup_p = sup.prototype;
let sub_p = sub.__proto__;
while(true){
if(sub_p == null){
return false
} else if(sub_p == sup_p){
return true
}
sub_p = sub.__proto__;
}
}
000:对象
1 :整数
010:浮点数
100:字符串
110:布尔
undefined:用 - (−2^30)表示。
null:对应机器码的 NULL 指针,一般是全零。
// 所以是分不清 Array 和 Object 的
Object.prototype.toString.call( )
// 这样可以检查区分开
Function.prototype.newCall = function (exector) {
exector.fn = this;
let res = exector.fn([...arguments].slice(1));
return res
}
Function.prototype.newApply = function(exector){
exector.fn = this;
let res = arguments[1] ? exector.fn(...arguments[1]) : exector.fn()
return res
}
Function.prototype.newBind = function(exector){
let self = this
let args = [...arguments].slice(1)
return function(){
self.apply(exector, args)
}
}
import | require | |
---|---|---|
规范 | ES6,需兼容 | CommonJS/AMD |
调用时间 | 编译时调用 | 运行时调用 |
本质 | 解构过程 | 赋值过程 |
function nnew(){
let newObj = new Object();
let constructor = [...arguments].shift();
let args = [...arguments].slice(1)
newObj.__proto__ == constructor.prototype;
return constructor.apply(newObj, args)
}
setTimeout
、setInterval
animation
requestAnimationFrame(function(){})
动画属性
animation-name 规定需要绑定到选择器的 keyframe 名称。
animation-duration 规定完成动画所花费的时间,以秒或毫秒计。
animation-timing-function 规定动画的速度曲线。
animation-delay 规定在动画开始之前的延迟。
animation-iteration-count 规定动画应该播放的次数。
animation-direction 规定是否应该轮流反向播放动画。
含义 | 默认 | 可选 | |
---|---|---|---|
flex-direction | 方向 | row | row 、row-reverse 、column 、column-reverse |
flex-wrap | 换行 | nowrap | nowrap、 wrap、 wrap-reverse |
flex-flow | 简写模式 | [row nowrap] | [ flex-direction flex-wrap] |
justify-content | 主轴对齐方式 | flex-start | flex-start、 flex-end、 center、 space-between(俩端对齐)、 space-around(分散对齐) |
align-items | 交叉轴对齐方式 | stretch | flex-start、 flex-end、 center、 baseline、 stretch |
align-content | 多轴对齐方式 | stretch | flex-start、 flex-end、 center、 space-between、 space-around、 stretch |
order | 排序 | 0 | 任意 |
flex-grow | 放大倍数 | 0(默认不放大) | 任意>0 |
flex-shrink | 缩小倍数 | 1(宽度不够默认缩小) | 任意>0 |
flex | 简写模式 | [0 1 auto] (后俩属性可不填) | [ <‘flex-grow’> ?<‘flex-shrink’> ?<‘flex-basis’> ] |
align-self | 自己对齐方式 | auto继承 | auto、 flex-start、 flex-end、 center、 baseline、 stretch |
// content + padding
element.clientWidth
element.clientHeight
// border
element.clientTop
element.clientLeft
// content + padding + border
element.offsetWidth
element.offsetHeight
// margin + 定位偏移
element.offsetTop
element.offsetLest
function reqest({
url,
method = 'GET',
headers = {},
params = {},
async = true,
}) {
return new Promise((reslove, rejexr) => {
let xhr = new XMLHttpRequest();
xhr.open(method, url, async);
Object.keys(headers).forEach(header => {
xhr.setRequestHeader(key, header[header])
});
xhr.send(params);
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
reslove(xhr.response)
}
}
})
}
function promise(exector) {
let self = this
self.value = null;
self.status = 'pedding';
self.callbacks = [];
function resolve(value) {
if (self.status == 'pedding') {
self.status = 'resolved';
self.value = value;
// 此处为异步方法,应比同步晚执行,放入异步队列。
setTimeout(() => {
self.callbacks.forEach(fn => {
fn.onResolved(self.value);
})
}, 0);
}
}
function reject(error) {
if (self.status == 'pedding') {
self.status = 'rejected';
self.value = error;
// 此处为异步方法,应比同步晚执行,放入异步队列。
setTimeout(() => {
self.callbacks.forEach(fn => {
fn.onRejected(self.value);
})
}, 0);
}
}
try {
exector(resolve, reject)
} catch (e) {
reject(e)
}
}
promise.prototype.then = function (onResolved, onRejected) {
let self = this
// 穿透
onResolved = typeof onResolved == 'function' ? onResolved : function () {return self.value }
onRejected = typeof onRejected == 'function' ? onRejected : function () {return self.value }
// 链式返回仍是 promise
return new promise((resolve, reject) => {
// 为了解决 resolve 被放入异步队列,状态还处于pedding的时候无法执行下面的方法。
if (self.status == 'pedding') {
self.callbacks.push({
onResolved: val => {
try {
let res = onResolved(self.value);
resolve(res)
} catch (e) {
reject(e)
}
},
onRejected: val => {
try {
let res = onRejected(self.value);
resolve(res)
} catch (e) {
reject(e)
}
}
})
}
if (self.status == 'resolved') {
setTimeout(() => {
try {
let res = onResolved(self.value);
resolve(res)
} catch (e) {
reject(e)
}
}, 0);
}
if (self.status == 'rejected') {
setTimeout(() => {
try {
let res = onRejected(self.value);
resolve(res)
} catch (e) {
reject(e)
}
}, 0);
}
})
}
promise.prototype.newAll = function(arr){
let result = [];
return new promise((reslove, resject)=>{
arr.forEach((item, index)=>{
item.then((value)=>{
result[index] = value;
if( result.length == arr.length){
reslove(result)
}
}, reject)
})
})
}
promise.prototype.newRace = function(arr){
return new prmose((reslove,reject)=>{
arr.forEach((item, index)=>{
item.then((value)=>{
reslove(value)
}, reject)
})
})
}
function jsonp(url, cb){
let script = document.createElement('script');
let script.src = `${url}?callback=${cb}`;
document.querySelector('head').appendChild(script);
cb = function(data){
console.log(data.toString())
}
}
// 添加监听
element.addEventListener(event, func, useCapture)
// event: 绑定的事件名
// func: 触发操作
// useCapture true-事件在捕获阶段执行;false-默认。事件在冒泡阶段执行
// 移除监听
element.removeEventListener(event, function,useCapture)
event.stopPropagation();
判断是否被观测, 没有就去监测
对象: 循环使用 Object.defineProperty(object, key, func()); 递归观测,对data 循环遍历,get的时候收集依赖,set的时候更新试图
数组: 重写数组方法,原型链重定向到重写的原型,调用原方法,同时通知试图更新。
解决问题:一个组件有非常多数据,如果修改多次或者修改多个数据,会触发多次页面渲染
解决方法:异步更新,dep.update => subs[i].update => queue => nextTick
把需要更新的组件放入队列,一个组件只存一次(根据组件id过滤),在 nextTick阶段 一次性渲染,会触发beforeupdate 和 updated 等钩子
nextTick
原理使用 Promise / MutationObserver / setImmediate / setTimeout
保证DOM渲染完成 后取DOM,可以获取最新值
异步渲染中使用到,queue会在nextTick中执行。
computed
(计算属性) 特点带缓存
和 watch 一样 都是一个 依赖(watcher)
lazy = dirty = true ,默认不会执行计算方法,在取值的时候才会执行,执行后 dirty = fasle, 下次直接取值。
计算属性内值变化,导致更新, dirty = true 下次取值再次计算。
beforeCreate 拿不到实例中的数据
Created 可以拿到数据,拿不到el,页面还未渲染,可发请求
beforeMount 挂载前 (SSR 无本阶段)
Mounted 挂载结束,可以拿到 DOM,通过新el替代旧el (SSR 无本阶段)
beforeUpdate 执行更新、重新渲染前,可以更改DOM,下个钩子一起更新
Updated 更新完成后,拿到最新DOM
beforeDestory 销毁前,取消定时器,事件绑定等等
先把页面语法(html+ js)转化为 AST Tree => 遍历对象 => 拼接成字符串 => new Function => render() => vdom
v-if
和 v-show
区别v-if 渲染时判断是否渲染该节点
v-show 通过display:none 判断
v-if
和 v-for
不要同时使用优先级: v-for > v-if ,循环渲染的时候会每次渲染都去判断,性能很低
diff
算法(双指针)同级比较vdom (key和标签名)
相同的话,复用olddom
再比较子节点,再递归比较
v-for
为什么要用 key
Vue 使用原地复用
删除的时候,会对预期结果进行检查,并可能出现删除最后一个的情况。
data
为什么是函数防止 data 被篡改
防止 子组件间的数据互相影响
click
和 click.native
.native修饰符是为了给组件绑定原生的事件方法。直接绑定无法触发
v-model
实现原理只对表单元素有用,可以说是 value+input
<temp v-model='check'></temp>
//实则传给子组件有个
model: {
prop:'check',
event:'input',
}
处理传过来的值,方法就是 event。
如果是表单组件,在编译的时候,会自己选择使用对应的方法来触发绑定的值。
v-html
问题导致xss攻击
内部子元素全部被覆盖
父 => 子:props
子 => 父:$on() + $emit()
直接获取组件实例: $parent, $children
依赖注入: provide, inject
Vuex
Vue
中相同的逻辑如果抽离(Vue.mixin)合并公共的 data,生命周期,方法…
都放在一个数组里,循环数组
mutation : 同步更新
action : 异步处理
//单入口
const config= {
entry: {
main: '../index.js' (main file path)
}
}
//多入口
const config = {
entry: {
pageOne: '../indexOne.js',
pageTwo: '../indexTwo.js',
}
}
module.exports = config;
const config= {
entry: {
main: '../index.js' (main file path)
},
+ output: {
+ filename: '[name].js',
+ path: __dirname+ '/dist'
+}
}
const config= {
entry: {
main: '../index.js' (main file path)
},
output: {
filename: '[name].js',
path: __dirname+ '/dist'
},
mode: 'development' // production
}
//在 俩个值中选择 ,或者在webpack --mode=production,打包的时候 CLI中传入
// 需要先安装对应模块
const config = {
......
module: {
rules: [
{ test: /\.css$/, use: 'css-loader' },
{ test: /\.ts$/, use: 'ts-loader' }
]
}
}
// 也可以一个规则配置多个loader
const config = {
......
module: {
rules: [
{ test: /\.css$/, use: [
{loader: 'css-loader'},
{loader: 'style-loader'}
]
{ test: /\.ts$/, use: 'ts-loader' }
]
}
}
const config = {
......
plugins: [
new webpack.optimize.UglifyJsPlugin(),
new HtmlWebpackPlugin({template: './src/index.html'})
]
}
// npm 安装后直接调用实例即可,设置自定义。
// 更多方法插件 https://www.webpackjs.com/plugins/
- HotModuleReplacementPlugin
热更新插件此插件为webpack自带插件
plugins: [
new webpack.HotModuleReplacementPlugin(),
]
- html-webpack-plugin
会把 entry 和 extract-text-webpack-plugin 中生成的css和js文件插入到生成的html中
const HtmlWebpackPlugin = require('html-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
filename: 'index.html',
template: path.join(__dirname, '/index.html'),
minify: {
// 压缩HTML文件
removeComments: true, // 移除HTML中的注释
collapseWhitespace: true, // 删除空白符与换行符
minifyCSS: true, // 压缩内联css
},
inject: true,
}),
]
- clean-webpack-plugin
删除上一次打包生成的buudle文件
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
plugins: [
new HtmlWebpackPlugin({
template: path.join(__dirname, '/index.html'),
}),
new CleanWebpackPlugin(), // 所要清理的文件夹名称
]
- extract-text-webpack-plugin
把css单独生成文件
const ExtractTextPlugin = require('extract-text-webpack-plugin')
plugins: [
new ExtractTextPlugin('css/index.css'),
]
5.UglifyJsPlugin
vue-vli 中使用的代码压缩插件
const UglifyJsPlugin = require('uglifyjs-webpack-plugin')
plugins: [
new UglifyJsPlugin({
uglifyOptions: {
compress: {
warnings: false
}
},
sourceMap: true, //是否启用文件缓存
parallel: true //使用多进程并行运行来提高构建速度
})
- DefinePlugin
定义全局变量
plugins: [
new webpack.DefinePlugin({
'process.env': 'development'
}),
]
// 直接引用
console.log(DESCRIPTION)
copy-webpack-plugin
会把在index.html中自己引入的静态文件,复制到dist目录中
const CopyWebpackPlugin = require('copy-webpack-plugin')
module.exports = {
plugins: [
new CopyWebpackPlugin({
patterns: [
{
from: 'public/js/*.js',
to: path.resolve(__dirname, 'dist', 'js'),
flatten: true,
},
],
}),
],
}
let deepClone = (obj) => {
let getType = (o) => {
if (o == null) {
return 'null';
} else if (0 == undefined) {
return 'undefined';
} else {
return Object.prototype.toString.call(o).slice(-7, -1)
}
}
let result;
if (getType(obj) == 'Object') {
result = {};
} else if (getType(obj) == 'Array') {
result = [];
} else {
return obj
}
for (let key in obj) {
let copy = obj[key];
if (getType(copy) == 'Object') {
result[key] = deepClone(copy)
} else if (getType(copy) == 'Array') {
result[key] = deepClone(copy)
} else {
result[key] = obj[key]
}
}
return result;
}
class student {
constructor(name) {
this.name = name;
}
getName = () => {
return this.name;
}
}
let singleMode = (()=>{
let instance = null;
return function(name){
if(!instance){
instance = new student(name)
}
return instance;
}
})();
let p = new singleMode('nys')