Giter Site home page Giter Site logo

blogg's People

blogg's Issues

前端优化学习笔记

通用优化手段汇总

  1. 业务代码打包优化

    异步引入

    ​ vue:vue cli生成的babel默认设置可查看项目@vue/babel-preset-app, 需要使用plugin-syntax-dynamic-import,然后在路由引用时,使用如下引用方式即可实现代码分包,且异步加载页面代码

    	() => import(组件路径)

    ​ react:react在16.6之后可以使用react.lazy搭配suspend实现异步加载,不过有缺陷。推荐采用的实现是loadable-components

    key

    ​ 有关key的vue文档描述:

    key 的特殊 attribute 主要用在 Vue 的虚拟 DOM 算法,在新旧 nodes 对比时辨识 VNodes。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。而使用 key 时,它会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。

    ​ 有相同父元素的子元素必须有独特的 key。重复的 key 会造成渲染错误。

    Mode: 'production'打包自带优化

    mode: development

    ​ 会将 process.env.NODE_ENV 的值设为 development。启用 NamedChunksPluginNamedModulesPlugin

    mode: production

    ​ 会将 process.env.NODE_ENV 的值设为 production。启用 FlagDependencyUsagePlugin, FlagIncludedChunksPlugin, ModuleConcatenationPlugin(scope hoisting), NoEmitOnErrorsPlugin, OccurrenceOrderPlugin, SideEffectsFlagPluginUglifyJsPlugin(tree shaking).

    tree shaking

    ​ tree shaking指的是通常用于打包时移除js中未引用的代码(dead-code),它依赖于ES6模块系统中的import 和 export 的静态结构特性

    ​ 开发时引入一个模块时,如果只引用其中一个功能,上线打包时只会把用到的功能打包进bundle中,其他没有用到的功能都不会打包进来,可以实现最简单的基本优化

    // Import everything (NOT TREE-SHAKABLE)
    import _ from 'lodash';
    // Import the item directly (CAN BE TREE SHAKEN)
    import debounce from 'lodash/lib/debounce';

    注意:sideEffect,指的是具有副作用的代码,此时无法被tree shaking掉 https://juejin.cn/post/6844903640533041159#heading-1

    为了出发webpack的tree shaking,你需要最基本的如下设置

    // Base Webpack Config for Tree Shaking
    const config = {
     mode: 'production',
     optimization: {
      usedExports: true,
      minimizer: [
       new TerserPlugin({...})
      ]
     }
    };

    上面的代码使用的是terser-webpack-plugin,vue-cli中使用的是uglyfyjs,create-react-app使用的是terser

    https://juejin.cn/post/6844903933370957831#heading-0,三个压缩工具的对比使用。

    为了利用 tree shaking 的优势, 你必须...

    • 使用 ES2015 模块语法(即 importexport)。
    • 确保没有编译器将您的 ES2015 模块语法转换为 CommonJS 的(顺带一提,这是现在常用的 @babel/preset-env 的默认行为,详细信息请参阅文档)。
    • 在项目的 package.json 文件中,添加 "sideEffects" 属性。
    • 使用 mode"production" 的配置项以启用更多优化项,包括压缩代码与 tree shaking。
    作用域提升(scope hoisting)

    ​ 由于scope hositing 需要分析出模块之间的依赖关系,因此源码必须使用ES6模块化语句,不然就不能生效,原因和 tree shaking一样

    css相关优化

    npm i -D mini-css-extract-plugin postcss-loader autoprefixer terser-webpack-plugin optimize-css-assets-webpack-plugin

    ​ Mini-css-extract-plugin 是用于将 CSS 提取为独立的文件的插件(不使用时,css被打在js里),对每个包含css的js文件都会创建一个css文件,支持按需加载css和sourceMap

    ​ 使用 postcss,需要使用 postcss-loader 和 autoprefixer

    ​ 需要使用 optimize-css-assets-webpack-plugin 插件来完成css压缩

    ​ 但是由于配置css压缩时会覆盖掉webpack默认的优化设置,导致JS代码无法压缩,所以还需要把JS代码压缩插件倒入进来 terser-webpack-plugin

  2. 依赖(vendor)打包优化

    按需引入

    ​ 图片按需加载,lazy-load

    ​ tree shaking

    开启gzip

    splitchunks

    https://juejin.cn/post/6844904198023168013

    动态polyfill

    ​ 什么是polyfill?

    ​ 不同浏览器,不同版本,运行js的结果可能会不同,polyfill就是为了抹平差异。

    [babel polyfill](https://www.imyangyong.com/blog/2020/03/babel/core-js@3之babel polyfill/) 存在诸多缺点,所以使用动态polyfill。使用polyfill-service。

    原理:每次打开页面,浏览器都会向Polyfill Service发送请求,Polyfill Service识别 User Agent,下发不同的 Polyfill,做到按需加载Polyfill的效果。

  3. 提升构建速度

    并行压缩,并行构建

    thread-loader多进程构建,多进程压缩(例:uglifyjs-webpack-plugin,开启 parallel 参数)

    提前打dll

参考文章:

https://github.com/vuejs/vue-cli/tree/dev/packages/@vue/babel-preset-app#readme -- vue cli生成项目里的默认babel设置

https://www.jianshu.com/p/22bd4eaf70b5 -- 使用loadable-components实现异步加载

https://juejin.cn/post/6844903895697735687 -- webpack优化汇总**

https://shanyue.tech/frontend-engineering/bundle.html -- 前端工程化总结 **

https://juejin.cn/post/6844904133430870024 -- 浅析key=index缺点

https://segmentfault.com/a/1190000018220850 -- scope hoisting

https://q.shanyue.tech/fe/html/1.html#%E6%96%B9%E6%A1%88%E4%B8%80-%E4%BD%8D%E7%BD%AE%E8%AE%A1%E7%AE%97-%E6%BB%9A%E5%8A%A8%E4%BA%8B%E4%BB%B6-scroll-dataset-api -- 图片lazy-load方案

https://www.imyangyong.com/blog/2020/06/webpack/webpack%20%E4%BC%98%E5%8C%96%E7%AD%96%E7%95%A5%E6%B1%87%E6%80%BB/ -- webpack优化汇总

https://juejin.cn/post/6844903549768302606 -- polyfill是什么

https://www.imyangyong.com/blog/2020/03/babel/core-js@3%E4%B9%8Bbabel%20polyfill/ -- babel polyfill

https://tsejx.github.io/webpack-guidebook/best-practice/optimization/dynamic-polyfill -- 动态polyfill的使用

test3

testtttttttttttttttttttttttttttt

几个想做的项目

image

亲戚拜年

篮球经理游戏

怪物猎人配装
6张表
弱特 精神抖擞 匠 超会心 攻击 体力 看破 集中
属性会心 挑战者 钢刃研磨

属性攻击,拔刀术,睡眠,麻痹,ko

应该有一个对应的珠子表

首先有优先技能 弱特 匠 拔刀术 睡眠麻痹ko 然后选高分装备 装备优先满足集中(考虑珠子)属性攻击有时不需要
主要还是要分析有几个孔

数据库表的添加
例如:对于头盔的技能,评分
image

前端界面的制作
点击搜索出来结果
结果的显示
设置条件
收藏一套装备
你有属性吗?上限有多高呢?武器有多少个孔呢

CREATE TABLE head(
name varchar(32),
rt int,
jsds int,
ji int,
chx int,
gj int,
tl int,
kp int,
jz int,
sxhx int,
tzz int,
gr int);

界面几个下拉按钮

写一个博客系统
模仿淘宝
5家店
购物车 双向绑定,使用vue.js
结账

写出完备的验证体系
博客系统的管理员设置
专门的管理员界面
专门的管理员表
可以看到所有的普通用户,可以查看用户的所有的评论,设置禁言(?)

时间管理软件
做一个前端界面
可不可以点击发送到我的手机?
点击截图
那就需要做一个登录
(添加数字时钟,更多内容)

es6学习笔记

es6学习笔记

let代替var,因为没有块级作用域

image

需要特别注意的一点

image
image
image
image
image

axios是使用的promise

箭头函数

image

箭头函数的作用主要还是方便

image
image

剩余参数

image

输出args时会输出剩余三项,但是只能放在最后一项

image

两种写法相同

解构赋值,太好用了

image
image
image

es6的数组,多了几个其他语言标配的数组

map,一个对一个,通过一个函数计算出来结果

image

配合箭头函数

image
image

reduce就是汇总

image
image

filter过滤器的使用

image
image

foreach

主要功能就是遍历

image

es6字符串

多了两个方法,startwith endswith

image
image

字符串模板,好用

使用返单引号````````

实现字符串链接功能

image

es6面向对象(建类,使用方法)

image

新的class

image

使用super进行继承父类的属性

面向对象的应用 react

react组件通过class存在

image

json的标准写法

json的简写

json的解析和串行化方法

image

异步和同步的优缺点

异步体验好

同步代码简单

使用promise

*用同步一样的方式书写异步代码

其实是封装了方法,更方便编写

image

最重要的就是这个all方法,一次执行多个ajax请求

generator函数

标志是函数名有*

image

踹一脚走一步

特别适合需要请求数据时的场景

yield主要用于分隔开数据处理的阶段,在同一个函数中进行分隔部分

image

ajax数据调用的几种方法

有逻辑的情况下promise更加麻烦

image

es6复习

image

继承时必须要执行父类的构造函数super

不然就会出现很多问题

image

手写代码相关

手写代码相关

1,es5模拟块级作用域

​ 在需要模拟的代码外面加自执行函数(function(){})

(function(){
  ...
})()

2,es5模拟const

使用object.defineProperty(),这个API用于在一个对象上增加或修改属性

// Object.defineProperty(obj, prop, desc) obj为要修改的对象 prop为目标key值 desc为描述
function _const(key, value) {    
    const desc = {        
        value,        
        writable: false    
    }    
    Object.defineProperty(window, key, desc)
}
    
_const('obj', {a: 1})   //定义obj
obj.b = 2               //可以正常给obj的属性赋值
obj = {}                //无法赋值新对象

3,手写call

​ call的用法...call(this指向, 参数1,参数2,...)

//变更函数调用者示例
function foo() {
    console.log(this.name)
}
const obj = {
    name: '写代码像蔡徐抻'
}
Function.prototype.myCall = function(thisArg, ...args) {
    thisArg.fn = this              // this指向调用call的对象,即我们要改变this指向的函数
    return thisArg.fn(...args)     // 执行函数并return其执行结果
}
foo.mycall(obj)

4,手写apply

​ apply用法:...apply(this指向, [...args]),所以实现与手写call类似

function foo(name) {
  console.log(name)
}
const obj = {}
Function.prototype.mycall = function(thisArg, args) {
  // 将函数挂载到目标this对象上
  thisArg.fn = this
  return thisArg.fn(...args)
}
foo.mycall(obj, ['123'])

5,手写bind

会创建一个新函数。当这个新函数被调用时,bind() 的第一个参数将作为它运行时的 this,之后的一序列参数将会在传递的实参前传入作为它的参数。

bind实现需要考虑实例化后对原型链的影响,从用法上看,似乎给call/apply包一层function就实现了bind():

Function.prototype.bind2 = function(thisArg, ...args) {
    let fn = this;
    let resFn = function() {
        return fn.apply(this instanceof resFn ? this : content,args)
    }
    function tmp() {}
    tmp.prototype = this.prototype;
    resFn.prototype = new tmp();
    //继承原型链
    return resFn;
}

6,手写防抖

// 防抖动函数
function debounce(fn,wait=50,immediate) {
    let timer;
    return function() {
        if(immediate) {
            fn.apply(this,arguments)
        }
        if(timer) clearTimeout(timer)
        timer = setTimeout(()=> {
            fn.apply(this,arguments)
        },wait)
    }
}

7,手写节流

function throttle(fn, wait) {
	let prev = new Date();
	return function() { 
	    const args = arguments;
		const now = new Date();
		if (now - prev > wait) {
			fn.apply(this, args);
			prev = new Date();
		}
	}

8,数组扁平化

1.ES6的flat()

const arr = [1, [1,2], [1,2,3]]
arr.flat(Infinity)  // [1, 1, 2, 1, 2, 3]
复制代码

2.序列化后正则

const arr = [1, [1,2], [1,2,3]]
const str = `[${JSON.stringify(arr).replace(/(\[|\])/g, '')}]`
JSON.parse(str)   // [1, 1, 2, 1, 2, 3]
复制代码

3.递归
对于树状结构的数据,最直接的处理方式就是递归

const arr = [1, [1,2], [1,2,3]]
function flat(arr) {
  let result = []
  for (const item of arr) {
    item instanceof Array ? result = result.concat(flat(item)) : result.push(item)
  }
  return result
}

flat(arr) // [1, 1, 2, 1, 2, 3]
复制代码

4.reduce()递归

const arr = [1, [1,2], [1,2,3]]
function flat(arr) {
  return arr.reduce((prev, cur) => {
    return prev.concat(cur instanceof Array ? flat(cur) : cur)
  }, [])
}

flat(arr)  // [1, 1, 2, 1, 2, 3]
复制代码

5.迭代+展开运算符

// 每次while都会合并一层的元素,这里第一次合并结果为[1, 1, 2, 1, 2, 3, [4,4,4]]
// 然后arr.some判定数组中是否存在数组,因为存在[4,4,4],继续进入第二次循环进行合并
let arr = [1, [1,2], [1,2,3,[4,4,4]]]
while (arr.some(Array.isArray)) {
  arr = [].concat(...arr);
}

console.log(arr)  // [1, 1, 2, 1, 2, 3, 4, 4, 4]

9,手写promise

function myPromise(constructor){
    let self=this;
    self.status="pending" //定义状态改变前的初始状态
    self.value=undefined;//定义状态为resolved的时候的状态
    self.reason=undefined;//定义状态为rejected的时候的状态
    function resolve(value){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.value=value;
          self.status="resolved";
       }
    }
    function reject(reason){
        //两个==="pending",保证了状态的改变是不可逆的
       if(self.status==="pending"){
          self.reason=reason;
          self.status="rejected";
       }
    }
    //捕获构造异常
    try{
       constructor(resolve,reject);
    }catch(e){
       reject(e);
    }
}

10,排序

sort

arr.sort((a, b) => a - b)

冒泡

function bubbleSort(arr){
  for(let i = 0; i < arr.length - 1; i++){
    for(let j = 0; j < arr.length - i - 1; j++){
      if(arr[j] > arr[j + 1]){
        swap(arr, j, j+1);
      }
    }
  }
  return arr;
}

快速

function quickSort(arr){
  if(arr.length <= 1){
    return arr;
  }
  let temp = arr[0];
  const left = [];
  const right = [];
  for(var i = 1; i < arr.length; i++){
    if(arr[i] > temp){
      right.push(arr[i]);
    }else{
      left.push(arr[i]);
    }
  }
  return quickSort(left).concat([temp], quickSort(right));
}

11,去重

Set去重

[...new Set(arr)]
Array.from(new Set(arr))

利用对象或Map的key去重

function arrayNonRepeatfy(arr) {
  let map = new Map();
  let array = new Array();  // 数组用于返回结果
  for (let i = 0; i < arr.length; i++) {
    if(map.has(arr[i])) {  // 如果有该key值
      map.set(arr[i], true); 
    } else { 
      map.set(arr[i], false);   // 如果没有该key值
      array.push(arr[i]);
    }
  } 
  return array ;
}
// 对象版本则为hasOwnProperty

for嵌套for,然后splice去重

indexOf,includes,filter去重

// 上面这两种其实都类似,都是双重循环
// 最简洁的是filter
function unique(arr) {
  return arr.filter(function(item, index, arr) {
    //当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
    return arr.indexOf(item, 0) === index;
  });
}

sort

sort方法的compareFunction如果不传的时候,元素按照转换为的字符串的各个字符的Unicode位点进行排序,所以如果两个值相等,则会在相邻位置。

function unique(arr) 
    arr = arr.sort()
    var arrry= [arr[0]];
    for (var i = 1; i < arr.length; i++) {
        if (arr[i] !== arr[i-1]) {
            arrry.push(arr[i]);
        }
    }
    return arrry;
}

12,实现instanceof

function instanceOf(left,right) {

    let proto = left.__proto__;
    let prototype = right.prototype
    while(true) {
        if(proto === null) return false
        if(proto === prototype) return true
        proto = proto.__proto__;
    }
}

13,实现函数柯里化

function curry(fn, args) {
    var length = fn.length;
    var args = args || [];
    return function(){
        newArgs = args.concat(Array.prototype.slice.call(arguments));
        if (newArgs.length < length) {
            return curry.call(this,fn,newArgs);
        }else{
            return fn.apply(this,newArgs);
        }
    }
}

14,实现new操作符

function New(func) {
    var res = {};
    if (func.prototype !== null) {
        res.__proto__ = func.prototype;
    }
    var ret = func.apply(res, Array.prototype.slice.call(arguments, 1));
    if ((typeof ret === "object" || typeof ret === "function") && ret !== null) {
        return ret;
    }
    return res;
}

博客实现笔记

博客实现笔记

只留一个header footer然后body的内容全部变化

博客数据获取

image

改变按钮的样式

在相应页面状态不可用

幽灵按钮

下面三个盒子的布局要换

从issue中去的数据

image

data返回的是一个数组

根据number进行排序

博文展示页面使用卡片

3个卡片

用户,数据,操作,结算

image

这样获得不同的URL值

image
{/blog/${getState[i].number}}
image
image

// {getState[number].body}

博客下一步思路

面包屑 ok

首页的图片换一下 走马灯效果,几个置顶文章

首页链接做一个新的组件ok

异步加载

redux重构

获取文章数据放在redux里

重构完就完事儿了

解决组件渲染出来数据渲染有延迟的问题

把整个dom当做props为state进行渲染

接下来就该做购物车了

点击一下给state加一个字符串key值

怎么获取到key呢?

key是card的一个props

image

split方法

记一次性能优化

记一次性能优化

第一步,分析问题

lighthouse

优化前得分
image-20220302185703316

chrome给出的优化建议:

  • Reduce unused JavaScript
  • Use HTTP/2
  • Avoid an excessive DOM size
  • Serve static assets with an efficient cache policy
  • Reduce JavaScript execution time
  • Minimize main-thread work

用户操作页面卡顿

优化前performence

image-20220302190018659

用户操作会有108.5ms的long task

第二步,开始优化

Reduce unused JavaScript

懒加载

react.lazy不支持ssr,需要使用loadable

项目中某些组件不能直接使用loadable实现懒加载,需要进行适配,这一期只实现了部分组件的懒加载。懒加载之后自动拆包:

255kb(gzipped) => 213kb(gzipped),vendor.js 934kb => 917kb(gzipped)

拆包vendor.js

从vendor.js拆出jshint、xlsx、regionselector

优化前1.64mb(gzipped) =>. 优化后934kb(gzipped)

Avoid an excessive DOM size

长列表懒加载

slice部分数据,scroll到底部时继续渲染。

取舍:优点是,react-beautiful-dnd生成的列表dom节点较多,导致页面打开慢,性能差。懒加载后节点少了很多,打开速度大幅提升。缺点是,滚动到底部时会重渲染列表,会有些许卡顿。

Reduce JavaScript execution time

减少组件reconcile时间

项目使用react-imvc开发,顶层store由context实现,代码内含有大量useModelState、useModelActions、useCtrl钩子函数,使用到了这些钩子函数后,该组件即成为context的订阅者,顶层context的任一数据更新,都会造成所有订阅者的更新。

解决办法:

1、使用connect替代上述hooks,将store中的数据已props的形式注入业务组件,最后使用React.memo包裹业务组件

2、使用useMemo对组件进行包裹

优化前

image-20220302194851698

优化后

image-20220302194908126

第三步,优化后测试

lighthouse测试

image-20220302195117255

performence

使用puppeteer对优化前后页面进行测试

优化前

image-20220302195352785

优化后

image-20220302195426956

Scripting、Rendering时间都有大幅减少,优化前出现两段long task影响用户体验,分别用时514.96ms和187.67ms,优化后的long task只有一段,耗时75.42ms

测试代码

const puppeteer = require('puppeteer');


(async () => {
const browser = await puppeteer.launch({
headless: false,
});
const page = await browser.newPage();
const navigationPromise = page.waitForNavigation();
// 登陆代码省略,登陆成功后开始模拟操作
await page.waitForNavigation({
waitUntil: 'networkidle0',
});
// 跳转到目标页面,省略
const testTarget = '#root > div > div > div > section > section > main > div.addnewpage-content > div.addnewpage-settings > div > div > div > div.addnewpage-settings-body > div:nth-child(1) > table > tbody > tr:nth-child(1) > td > div > div.ant-col.ant-col-24 > input';
await page.waitForSelector(testTarget, {visible: true, timeout: 3000});
await page.tracing.start({path: 'trace.json', screenshots: true});
await page.focus(testTarget);
await page.type(testTarget, '2', {delay: 1000});
await page.tracing.stop();

await browser.close();
})();

react学习记录1

image
image

生命周期的代码

使用了setinterval函数

image

子节点

image

组件嵌套

image

前端基础复习笔记

基础复习笔记

0,js面向对象

见参考文章

1,v8引擎机制(node)

img

img

经过多年演进,V8 目前形成了由解析器、基线编译器 Ignition 和优化编译器 TurboFan 组成的 JavaScript 执行管道。

  • 引用计数:给一个变量赋值引用类型,则该对象的引用次数+1,如果这个变量变成了其他值,那么该对象的引用次数-1,垃圾回收器会回收引用次数为0的对象。但是当对象循环引用时,会导致引用次数永远无法归零,造成内存无法释放。

  • 标记清除:垃圾收集器先给内存中所有对象加上标记,然后从根节点开始遍历,去掉被引用的对象和运行环境中对象的标记,剩下的被标记的对象就是无法访问的等待回收的对象。

v8引擎的回收

img

栈内存里存储基本类型数据以及引用类型数据的内存地址,堆内存储存引用类型的数据。

JS相较于C++等语言为什么慢,V8做了哪些优化
  1. JS的问题:
    • 动态类型:导致每次存取属性/寻求方法时候,都需要先检查类型;此外动态类型也很难在编译阶段进行优化
    • 属性存取:C++/Java等语言中方法、属性是存储在数组中的,仅需数组位移就可以获取,而JS存储在对象中,每次获取都要进行哈希查询
  2. V8的优化:
    • 优化JIT(即时编译):相较于C++/Java这类编译型语言,JS一边解释一边执行,效率低。V8对这个过程进行了优化:如果一段代码被执行多次,那么V8会把这段代码转化为机器码缓存下来,下次运行时直接使用机器码。
    • 隐藏类:对于C++这类语言来说,仅需几个指令就能通过偏移量获取变量信息,而JS需要进行字符串匹配,效率低,V8借用了类和偏移位置的**,将对象划分成不同的组,即隐藏类
    • 内嵌缓存:即缓存对象查询的结果。常规查询过程是:获取隐藏类地址 -> 根据属性名查找偏移值 -> 计算该属性地址,内嵌缓存就是对这一过程结果的缓存
    • 垃圾回收管理:上文已介绍

2,浏览器渲染机制

解析:html和css各自的parser进行解析,生成domTree和styleRules,合并产生renderTree

layout(回流):根据renderTree就能知道所有节点的样式,计算节点在页面上的大小和位置

painting(重绘):绘制页面

dom操作的重要优化手段,要尽量避免回流,只进行重绘,首次渲染则无法避免layout。

何时发生回流:

  • 添加或删除可见的DOM元素
  • 元素的位置发生变化
  • 元素的尺寸发生变化(包括外边距、内边框、边框大小、高度和宽度等)
  • 内容发生变化,比如文本变化或图片被另一个不同尺寸的图片所替代。
  • 页面一开始渲染的时候(这肯定避免不了)
  • 浏览器的窗口尺寸变化(因为回流是根据视口的大小来计算元素的位置和大小的)

现代浏览器会对频繁的回流或重绘操作进行优化,浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,所以需要返回最新的布局信息的属性和方法,浏览器不得不清空队列,进行一次批处理,这样可以把多次回流和重绘变成一次。

一些优化手段

CSS:

  • 避免使用table布局。
  • 尽可能在DOM树的最末端改变class。
  • 避免设置多层内联样式。
  • 将动画效果应用到position属性为absolutefixed的元素上
  • 避免使用CSS表达式(例如:calc()
  • CSS3硬件加速(GPU加速)

JavaScript:

  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘

  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来

  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流

3,网络相关

​ 状态码

​ 2xx 开头(请求成功)

200 OK:客户端发送给服务器的请求被正常处理并返回

3xx 开头(重定向)

301 Moved Permanently:永久重定向,请求的网页已永久移动到新位置。 服务器返回此响应时,会自动将请求者转到新位置

302 Moved Permanently:临时重定向,请求的网页已临时移动到新位置。服务器目前从不同位置的网页响应请求,但请求者应继续使用原有位置来进行以后的请求

304 Not Modified:未修改,自从上次请求后,请求的网页未修改过。服务器返回此响应时,不会返回网页内容

4xx 开头(客户端错误)

400 Bad Request:错误请求,服务器不理解请求的语法,常见于客户端传参错误

401 Unauthorized:未授权,表示发送的请求需要有通过 HTTP 认证的认证信息,常见于客户端未登录

403 Forbidden:禁止,服务器拒绝请求,常见于客户端权限不足

404 Not Found:未找到,服务器找不到对应资源

5xx 开头(服务端错误)

500 Inter Server Error:服务器内部错误,服务器遇到错误,无法完成请求

501 Not Implemented:尚未实施,服务器不具备完成请求的功能

502 Bad Gateway:作为网关或者代理工作的服务器尝试执行请求时,从上游服务器接收到无效的响应。

503 service unavailable:服务不可用,服务器目前无法使用(处于超载或停机维护状态)。通常是暂时状态。

4,前端安全

xss(跨站脚本攻击)

​ 突出一个执行脚本

​ XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM。

  • ​ 反射型(非持久型)

    攻击者可以购买存在漏洞网站的广告,将恶意链接插入在广告的链接中,注入脚本进入被攻击者的网站。用户点击一个恶意链接,提交一个表单,或者进入一个恶意网站时,都可以注入脚本。

    例:访问一个链接,服务端返回一个可执行脚本

    const http = require('http');
    function handleReequest(req, res) {
        res.setHeader('Access-Control-Allow-Origin', '*');
        res.writeHead(200, {'Content-Type': 'text/html; charset=UTF-8'});
        res.write('<script>alert("反射型 XSS 攻击")</script>');
        res.end();
    }
    
    const server = new http.Server();
    server.listen(8001, '127.0.0.1');
    server.on('request', handleReequest);
    // script中可以是获取用户数据,比如说用户cookie,token
  • ​ 存储型(持久型)

    攻击者将脚本作为输入的数据 "存储" 在被攻击网站的服务器端,当浏览器请求数据时,脚本从服务器上传回并执行,获取浏览者的数据

  • ​ 基于DOM

    基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击:

    网站dom元素存在可被修改风险,将网站dom元素篡改后对服务器进行攻击

如何防范

HttpOnly 防止劫取 Cookie:浏览器将禁止页面的Javascript 访问带有 HttpOnly 属性的Cookie。通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持。

输入检查:不要相信用户的任何输入。 对于用户的任何输入要进行检查、过滤和转义。而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如 <,>,script,防止 XSS 攻击:

// vuejs 中的 decodingMap
// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&amp;': '&',
  '&#10;': '\n'
}

输出检查:用户的输入会存在问题,服务端的输出也会存在问题。例如利用 sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

CSRF/XSRF(跨站请求伪造)

劫持受信任用户向服务器发送非预期请求的攻击方式。

突出一个获取用户数据,发出请求

CSRF攻击防范

Referer Check:根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。通过 Referer Check,可以检查请求是否来自合法的"源"。

添加token验证:要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

验证码:验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。为什么呢,因为首先防范肯定会优先使用上面两种方式,然后验证码强制人机交互,确认是一个人类在访问网站。从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。

5,设计模式

设计模式 描述 例子
单例模式 一个类只能构造出唯一实例 Redux/Vuex的store
工厂模式 对创建对象逻辑的封装 jQuery的$(selector)
观察者模式 当一个对象被修改时,会自动通知它的依赖对象 Redux的subscribe、Vue的双向绑定
装饰器模式 对类的包装,动态地拓展类的功能 React高阶组件、ES7 装饰器
适配器模式 兼容新旧接口,对类的包装 封装旧API
代理模式 控制对象的访问 事件代理、ES6的Proxy

6,html相关

html5新特性

标签:新增语义化标签(aside / figure / section / header / footer / nav等),增加多媒体标签videoaudio,使得样式和结构更加分离

属性:增强表单,主要是增强了input的type属性;meta增加charset以设置字符集;script增加async以异步加载脚本

存储:增加localStoragesessionStorageindexedDB,引入了application cache对web和应用进行缓存

API:增加拖放API地理定位SVG绘图canvas绘图Web WorkerWebSocket

几种前端储存以及它们之间的区别

  • cookies: HTML5之前本地储存的主要方式,大小只有4k,HTTP请求头会自动带上cookie,兼容性好
  • localStorage:HTML5新特性,持久性存储,即使页面关闭也不会被清除,以键值对的方式存储,大小为5M
  • sessionStorage:HTML5新特性,操作及大小同localStorage,和localStorage的区别在于sessionStorage在选项卡(页面)被关闭时即清除,且不同选项卡之间的sessionStorage不互通
  • IndexedDB: NoSQL型数据库,类比MongoDB,使用键值对进行储存,异步操作数据库,支持事务,储存空间可以在250MB以上,但是IndexedDB受同源策略限制
  • Web SQL:是在浏览器上模拟的关系型数据库,开发者可以通过SQL语句来操作Web SQL,是HTML5以外一套独立的规范,兼容性差

href和src有什么区别

href(hyperReference)即超文本引用:当浏览器遇到href时,会并行的地下载资源,不会阻塞页面解析,例如我们使用引入CSS,浏览器会并行地下载CSS而不阻塞页面解析. 因此我们在引入CSS时建议使用而不是@import

<link href="style.css" rel="stylesheet" />

src(resource)即资源,当浏览器遇到src时,会暂停页面解析,直到该资源下载或执行完毕,这也是script标签之所以放底部的原因

<script src="script.js"/>

meta有哪些属性,作用是什么

meta标签用于描述网页的元信息,如网站作者、描述、关键词,meta通过name=xxxcontent=xxx的形式来定义信息,常用设置如下:

  • charset:定义HTML文档的字符集
 <meta charset="UTF-8" >
  • http-equiv:可用于模拟http请求头,可设置过期时间、缓存、刷新
<meta http-equiv="expires" content="Wed, 20 Jun 2019 22:33:00 GMT"
  • viewport:视口,用于控制页面宽高及缩放比例
<meta 
    name="viewport" 
    content="width=device-width, initial-scale=1, maximum-scale=1"
>

viewport有哪些参数,作用是什么

  • width/height,宽高,默认宽度980px
  • initial-scale,初始缩放比例,1~10
  • maximum-scale/minimum-scale,允许用户缩放的最大/小比例
  • user-scalable,用户是否可以缩放 (yes/no)

http-equive属性的作用和参数

expires,指定过期时间

progma,设置no-cache可以禁止缓存

refresh,定时刷新

set-cookie,可以设置cookie

X-UA-Compatible,使用浏览器版本

apple-mobile-web-app-status-bar-style,针对WebApp全屏模式,隐藏状态栏/设置状态栏颜色

7,事件循环

js是单线程语言,单线程意味着,javascript代码在执行的任何时候,都只有一个主线程来处理所有的任务。

单线程是必要的,也是javascript这门语言的基石,原因之一在其最初也是最主要的执行环境——浏览器中,我们需要进行各种各样的dom操作。试想一下 如果javascript是多线程的,那么当两个线程同时对dom进行一项操作,例如一个向其添加事件,而另一个删除了这个dom,此时该如何处理呢?因此,为了保证不会 发生类似于这个例子中的情景,javascript选择只用一个主线程来执行代码,这样就保证了程序执行的一致性。

javascript的另一个特点是“非阻塞”,通过事件循环实现。

当javascript代码执行的时候会将不同的变量存于内存中的不同位置:堆和栈。堆里存放着一些对象。而栈中则存放着一些基础类型变量以及对象的指针。同时还存在js执行时的执行环境,又叫执行上下文,js的运行会产生很多执行上下文,但是因为js为单线程,所以这个执行上下文只能被放在一个地方,叫执行栈。栈都是遵循先进后出的顺序进行调用和销毁。

上述为同步代码的执行,执行栈遇到异步任务时,不会阻塞,继续执行,异步返回后被加入事件队列。

当执行栈执行完毕清空时,js线程去事件队列执行。

宏任务和微任务

事件队列还分为宏任务队列,微任务队列

以下事件属于宏任务:

  • setInterval()
  • setTimeout()

以下事件属于微任务

  • new Promise()
  • new MutaionObserver()

js执行栈为空时,主进程会先找微任务队列,有则执行至空,没有才去宏任务队列找,如果属于宏任务的代码中带有异步代码,那么就会在事件队列中添加对应队列,到下一个轮回执行

node下的事件循环

大体与浏览器环境下一致,👇node环境的特殊执行顺序

外部输入数据-->轮询阶段(poll)-->检查阶段(check)-->关闭事件回调阶段(close callback)-->定时器检测阶段(timer)-->I/O事件回调阶段(I/O callbacks)-->闲置阶段(idle, prepare)-->轮询阶段...

我的理解:poll阶段为node使用的查询事件队列是否有事件的任务,有两种情况poll阶段会终止执行poll queue中的下一个回调:1.所有回调执行完毕。2.执行数超过了node的限制。

check阶段

check阶段专门用来执行setImmediate()方法的回调,当poll阶段进入空闲状态,并且setImmediate queue中有callback时,事件循环进入这个阶段。

close阶段

当一个socket连接或者一个handle被突然关闭时(例如调用了socket.destroy()方法),close事件会被发送到这个阶段执行回调。否则事件会用process.nextTick()方法发送出去。

8,跨域相关

什么是跨域?

浏览器默认采用的都是同源策略

  • 同源策略限制、安全性考虑
  • 协议、IP和端口不一致都是跨域行为

实现跨域的方法

jsonp

原理:在HTML标签里,一些标签比如script、img这样的获取资源的标签是没有跨域限制的,利用这一点,我们可以这样干:

后端

// 处理成功失败返回格式的工具
const {successBody} = require('../utli')
class CrossDomain {
  static async jsonp (ctx) {
    // 前端传过来的参数
    const query = ctx.request.query
    // 设置一个cookies
    ctx.cookies.set('tokenId', '1')
    // query.cb是前后端约定的方法名字,其实就是后端返回一个直接执行的方法给前端,由于前端是用script标签发起的请求,所以返回了这个方法后相当于立马执行,并且把要返回的数据放在方法的参数里。
    ctx.body = `${query.cb}(${JSON.stringify(successBody({msg: query.msg}, 'success'))})`
  }
}
module.exports = CrossDomain

前端

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
  </head>
  <body>
    <script type='text/javascript'>
      // 后端返回直接执行的方法,相当于执行这个方法,由于后端把返回的数据放在方法的参数里,所以这里能拿到res。
      window.jsonpCb = function (res) {
        console.log(res)
      }
    </script>
    <script src='http://localhost:9871/api/jsonp?msg=helloJsonp&cb=jsonpCb' type='text/javascript'></script>
  </body>
</html>

cors

原理:CORS 是 HTTP 的一部分,它允许服务端来指定哪些主机可以从这个服务端加载资源。

跨源资源共享 (CORS) (或通俗地译为跨域资源共享)是一种基于HTTP 头的机制,该机制通过允许服务器标示除了它自己以外的其它origin(域,协议和端口),这样浏览器可以访问加载这些资源。

9,cookie,session

  1. 由于HTTP协议是无状态的协议,所以服务端需要记录用户的状态时,就需要用某种机制来识具体的用户,这个机制就是Session.典型的场景比如购物车,当你点击下单按钮时,由于HTTP协议无状态,所以并不知道是哪个用户操作的,所以服务端要为特定的用户创建了特定的Session,用用于标识这个用户,并且跟踪用户,这样才知道购物车里面有几本书。这个Session是保存在服务端的,有一个唯一标识。在服务端保存Session的方法很多,内存、数据库、文件都有。集群的时候也要考虑Session的转移,在大型的网站,一般会有专门的Session服务器集群,用来保存用户会话,这个时候 Session 信息都是放在内存的,使用一些缓存服务比如Memcached之类的来放 Session。
  2. 思考一下服务端如何识别特定的客户?这个时候Cookie就登场了。每次HTTP请求的时候,客户端都会发送相应的Cookie信息到服务端。实际上大多数的应用都是用 Cookie 来实现Session跟踪的,第一次创建Session的时候,服务端会在HTTP协议中告诉客户端,需要在 Cookie 里面记录一个Session ID,以后每次请求把这个会话ID发送到服务器,我就知道你是谁了。有人问,如果客户端的浏览器禁用了 Cookie 怎么办?一般这种情况下,会使用一种叫做URL重写的技术来进行会话跟踪,即每次HTTP交互,URL后面都会被附加上一个诸如 sid=xxxxx 这样的参数,服务端据此来识别用户。
  3. Cookie其实还可以用在一些方便用户的场景下,设想你某次登陆过一个网站,下次登录的时候不想再次输入账号了,怎么办?这个信息可以写到Cookie里面,访问网站的时候,网站页面的脚本可以读取这个信息,就自动帮你把用户名给填了,能够方便一下用户。这也是Cookie名称的由来,给用户的一点甜头。
    所以,总结一下:
    Session是在服务端保存的一个数据结构,用来跟踪用户的状态,这个数据可以保存在集群、数据库、文件中;
    Cookie是客户端保存用户信息的一种机制,用来记录用户的一些信息,也是实现Session的一种方式。

Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。Cookie 主要用于以下三个方面:

  • 会话状态管理(如用户登录状态、购物车、游戏分数或其它需要记录的信息)
  • 个性化设置(如用户自定义设置、主题等)
  • 浏览器行为跟踪(如跟踪分析用户行为等)

而浏览器所持有的 Cookie 分为两种:

  • Session Cookie(会话期 Cookie):会话期 Cookie 是最简单的Cookie,它不需要指定过期时间(Expires)或者有效期(Max-Age),它仅在会话期内有效,浏览器关闭之后它会被自动删除。
  • Permanent Cookie(持久性 Cookie):与会话期 Cookie 不同的是,持久性 Cookie 可以指定一个特定的过期时间(Expires)或有效期(Max-Age)。
res.setHeader('Set-Cookie', ['mycookie=222', 'test=3333; expires=Sat, 21 Jul 2018 00:00:00 GMT;']);

上述代码创建了两个 Cookie:mycookie 和 test,前者属于会话期 Cookie,后者则属于持久性 Cookie。

10,get和post,以及其他的调用接口方法

从字面意思理解,get为“读取“一个资源。比如Get到一个html文件。反复读取不应该对访问的数据有副作用。比如”GET一下,用户就下单了,返回订单已受理“,因为GET因为是读取,就可以对GET请求的数据做缓存。这个缓存可以做到浏览器本身上(彻底避免浏览器发请求),也可以做到代理上(如nginx),或者做到server端(用Etag,至少可以减少带宽消耗)

post为发出一条消息,让服务器做一件事。这件事往往是有副作用的,意味着不能随意多次执行。因此也就不能缓存。比如通过POST下一个单,服务器创建了新的订单,然后返回订单成功的界面。

PUT与POST的区别在于,PUT的实际语义是“replace”replace。REST规范里提到PUT的请求体应该是完整的资源,包括id在内,用来创建一个URI已知的资源,或对已知资源进行完全替换。

PATCH一般是用来局部更新资源的

11,js严格模式

几个需要重视的点:

全局变量显式声明

在正常模式中,如果一个变量没有声明就赋值,默认是全局变量。严格模式禁止这种用法,全局变量必须显式声明。

​ "use strict";

  v = 1; // 报错,v未声明

  for(i = 0; i < 2; i++) { // 报错,i未声明
  }

增强的安全措施

(1)禁止this关键字指向全局对象

(2)禁止在函数内部遍历调用栈

(3)对象不能有重名的属性

(4)函数不能有重名的参数

(5)函数必须声明在顶层

12,undefined和null

目前,null和undefined基本是同义的,只有一些细微的差别。

undefined是基本数据类型,null是引用数据类型

**null表示"没有对象",即该处不应该有值。**典型用法是:

(1) 作为函数的参数,表示该函数的参数不是对象。

(2) 作为对象原型链的终点。

Object.getPrototypeOf(Object.prototype)
// null

**undefined表示"缺少值",就是此处应该有一个值,但是还没有定义。**典型用法是:

(1)变量被声明了,但没有赋值时,就等于undefined。

(2) 调用函数时,应该提供的参数没有提供,该参数等于undefined。

(3)对象没有赋值的属性,该属性的值为undefined。

(4)函数没有返回值时,默认返回undefined。

var i;
i // undefined

function f(x){console.log(x)}
f() // undefined

var  o = new Object();
o.p // undefined

var x = f();
x // undefined

13,cjs和es module

ES6 模块,简称 ESM;另一种是 Node.js 专用的 CommonJS 模块,简称 CJS。这两种模块不兼容。

差异:

语法上面,CommonJS 模块使用require()加载和module.exports输出,ES6 模块使用importexport

用法上面,require()是同步加载,后面的代码必须等待这个命令执行完,才会执行。import命令则是异步加载,或者更准确地说,ES6 模块有一个独立的静态解析阶段,依赖关系的分析是在那个阶段完成的,最底层的模块第一个执行。

区分:

Node.js 要求 ES6 模块采用.mjs后缀文件名。也就是说,只要脚本文件里面使用import或者export命令,那么就必须采用.mjs后缀名。Node.js 遇到.mjs文件,就认为它是 ES6 模块,默认启用严格模式,不必在每个模块文件顶部指定"use strict"

如果不希望将后缀名改成.mjs,可以在项目的package.json文件中,指定type字段为module

cjs只能加载.cjs模块

cjs模块加载es module

CommonJS 的require()命令不能加载 ES6 模块,会报错,只能使用import()这个方法加载。

(async () => {
  await import('./my-app.mjs');
})();

上面代码可以在 CommonJS 模块中运行。

require()不支持 ES6 模块的一个原因是,它是同步加载,而 ES6 模块内部可以使用顶层await命令,导致无法被同步加载。

es module加载cjs模块

ES6 模块的import命令可以加载 CommonJS 模块,但是只能整体加载,不能只加载单一的输出项。

// 正确
import packageMain from 'commonjs-package';

// 报错
import { method } from 'commonjs-package';

这是因为 ES6 模块需要支持静态代码分析,而 CommonJS 模块的输出接口是module.exports,是一个对象,无法被静态分析,所以只能整体加载。

加载单一的输出项,可以写成下面这样。

import packageMain from 'commonjs-package';
const { method } = packageMain;

同时支持两种模块

一个模块同时要支持 CommonJS 和 ES6 两种格式,也很容易。

如果原始模块是 ES6 格式,那么需要给出一个整体输出接口,比如export default obj,使得 CommonJS 可以用import()进行加载。

如果原始模块是 CommonJS 格式,那么可以加一个包装层。

import cjsModule from '../index.js';
export const foo = cjsModule.foo; 

上面代码先整体输入 CommonJS 模块,然后再根据需要输出具名接口。

你可以把这个文件的后缀名改为.mjs,或者将它放在一个子目录,再在这个子目录里面放一个单独的package.json文件,指明{ type: "module" }

另一种做法是在package.json文件的exports字段,指明两种格式模块各自的加载入口。

"exports"{ 
    "require": "./index.js"
    "import": "./esm/wrapper.js" 
}

上面代码指定require()import,加载该模块会自动切换到不一样的入口文件。

参考文章:

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_encapsulation.html --- js面向对象1

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance.html --- js面向对象2

http://www.ruanyifeng.com/blog/2010/05/object-oriented_javascript_inheritance_continued.html --- js面向对象3

https://zhuanlan.zhihu.com/p/33058983 --- 事件循环

https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy --- 浏览器的同源策略

https://segmentfault.com/a/1190000015597029 --- 跨域相关

http://www.ruanyifeng.com/blog/2013/01/javascript_strict_mode.html --- 严格模式

http://www.ruanyifeng.com/blog/2020/08/how-nodejs-use-es6-module.html --- cjs和es module

https://juejin.cn/post/6844903928442667015 --- 前端基础0

https://juejin.cn/post/6844904116552990727 --- 前端基础1

https://juejin.cn/post/6844903776512393224 --- 前端基础2

https://juejin.cn/post/6844904103504527374 --- 前端基础3

https://juejin.cn/post/6844903885488783374 --- 前端面试题

购物车实现笔记

购物车实现笔记

展示数据

计算

展示

先做一个购物车小原型

几个商品,然后有一个点击加入购物车按钮(表单提交)

购物车结算

在action中获取数据

image

axios是获取网络上数据的方式

本地获取还是要使用fetch

../是相对于index.js而言的,所以要这样写

image

在fetch中不能setState??

不能使用function,需要使用箭头函数

完成了数据的输出

主页列出所有商品,每个卡片有一个加入购物车按键,加入后按钮字变化成取消加入,有一个购物车按钮进入购物车,购物车有结算功能

还是做有左侧边栏的吧

先完成商品展示和购物结算

用户

在哪里获取数据

component只能进行数据展示

通过props传入数据

mapstatetoprops?

mapdispatchtoprops

不使用redux获取数据只需要使用setstate,所以也是使用state

不能在reducer中使用axios

componentdidmount中dispatch()

做到了点击添加购物车按钮响应这一步

应该给action传一个参数?????

在dispatch中可以传入ownprops可以获取到keys,再传给action

最后一步写到reducer然后呢??

就像todolist中添加todo一样,点击一次就往store添加key

react-thunk的作用,举例:当点击分类按钮时,就需要进行异步数据获取,异步action

思考一下获取分类后的数据怎么做

点击按钮发送action,action应该怎么写,reducer又不能请求数据,好像action可以请求

不使用thunk的时候action是一个对象

使用了thunk之后action是一个函数

可以传入dispatch

每个card都是触发同一个按钮事件?

是的,只要key不一样就行

数据通过params传递过去

嘻嘻

先做无redux版本,点击按钮获取到key

现在的问题是如何获取到key的值

鞋子的id,点击后将id转为字符串jiaru

点击时怎么获取id 提取出来

要用到回调函数

子组件向父组件传递数据

将子组件的props--key传出去

进行基本的react事件响应复习

购物车显示界面

首先得到传过来的id号

然后筛选

计算总价

一双或几双的计算

要将set转化为array

要加载图片啊

做结算功能

哪一种才能得到event.target.value

image

数字输入框直接使用就能获得数值

image

用户界面

做一个登录

连数据库(保存用户数据,也许该改代码为从数据库获取数据?)

查看买了什么东西(这个也是 保存在数据库)

原js都是表单提交后交给php然后在服务器端进行操作,是怎么获得json中数据的

使用axios获取

现在我在做的都是spa,都是显示端的,后台的不会搞,但是要获取数据还是得有数据库

还是看mogodb好了

研究mongodb的增删改查

实现用户的登录数据验证,添加用户

购物车数据显示 功能

怎么做一个简单的验证demo

console。log登录成功 输入框按键添加数据

react怎么使用value来着。。。

原来mongoose是在后端express中使用的,在后端取得数据后再给前端

现在已经可以登录成功

研究怎么从在express中进行增删改查操作

image
image

实现查询了,现在解决如何传输数据到前端react

打通前端后台,又回到redux了。。。

学习使用thunk和fetch

在action中返回一个dispatch

在简单的express打通redux例子中报错,说的是babel的错误,但是前段部分基本都懂,所以是express的部分

所以又回到了学习express部分

mongodb创建一个集合

image

从数据库获取数据

后台

image

前端

image

路由出现问题

每次都会进行根目录上的查询工作

没问题,打错了

现在完成注册传送给服务器数据

查询服务器然后进行判断是登陆还是注册

保持一个登陆的状态还是要使用cookie?

先去做另一个项目好了

完成了

登录功能的后台实现

设置好了cookie

主页上有登录按键变为用户信息按键

改造数据库变成购物车数据可以提交到数据库

点击按钮进入用户界面,没有cookie就是登录界面

更改数据库结构

加上用户购买商品这个选项

怎么将用户购买了什么产品添加到数据库中,怎么实现点击一个按钮发送一次数据

使用axios

image

终于通过axios让后台拿到了用户购买物品数据

尝试将其插入数据库

插入到数据库中了

实现用户页面显示购物车信息,从数据库取出数据

login_page加载的时候,发送username

显示商品,组件装载时获取

但是在设置state的时候是异步加载的

解决了

购物车这里根据cookie进行数据库取出数据

急需解决数据库问题

不能正常修改数据库

user页面问题大

update语句写错了

完成了

实现用户退出登录功能

删除cookie

还剩下什么功能?

用户页面显示购买物品(时间?)

购物物品的删除功能

退出登录功能

用户按钮的字换为用户名

调整标签栏的bug

image

express主要就是因为路由比较强

image

动态路由

3,4使用路由传递参数

index和user的作用

因为有proxys,所以

image

访问3001时,返回express的首页,访问users时则是返回数据

image
image

这样能访问得到

完成了上传数据

将上传过来的数据存储到数据库中

image

反正模板和数据相对应,其他的无所谓

res.send返回一个页面

现在做登录功能,简单的判断一下就行了

不做了

用数据库重构购物车吧!(将买了什么东西先保存在数据库中,之后再使用cookie)

完成了数据从数据库获取的功能

该做用户功能了,登录,保存用户选的商品(都通过数据库)

图片的处理

这样一来购物车功能就全部完成了

干脆所有操作购物车的行为都会影响到数据库?

mongodb学习笔记

mongodb学习笔记

js文件链接数据库

image

插入一条记录

image

第二个参数是哪一个集合

image
image

查找一个集合里面的记录

image

循环加入1000条数据

image

效率更好的写法

数据初始化为一个对象

image

然后放到数组中

image

修改数据

image# 只有这样修改才是对的

update修改器

set修改器 修改特定属性

image
image

unset修改器 去掉特定的属性

image

值写什么其实都无所谓

inc修改器 数据操作

image

multi修改器

image

上面是给workmate的所有属性都加上interset

upset

image

没找到就直接添加

数组修改器

push

就像数组一样

image

ne修饰符

没有则添加

image

上面因为interest中没有game所以添加game,则不会添加playgame

升级版 addtoset

查找属性如果没有则添加

image

each批量追加

image

pop删除固定位置的信息

image

上例就是删除了interest最后一个信息

数组定位修改

image

从0开始,上例是修改了第三位元素

实际上这样修改元素是很少的

非应答式操作

不好

应答式操作

db.runcommand()

image

上例给所有sex=1的添加了1000第一个false表示没找到就不添加,第二个true表示全选而不是只操作一个

db.runcommand()则返回了一个结果

形成应答式操作

findandmodify

修改哪个集合

image

查询语句

image

_id:0设置的是不显示id那一大串

image

结果如上

加上使用不等修饰符

image

多条件查询

image

查询年龄是25,30,33的人

或者查询

image

or

并且查询

image

and

非查询

image

数组查询

image

[ ]这样只会完全匹配

要使用到all

image

喜欢看电影和读书的人

使用in

image

喜欢看书或喜欢看电影,并不是使用or

size是数量

image

slice显示选项

image
image

下面是一个简单的分页操作的实现

image

where的使用

image

容易被黑,性能差

print和printjson的区别?

正式使用js进行操作,原来都是复制进终端进行查询

image

hasnext

或者是foreach

image

使用索引

image

通用的随机数生成方法

image

生成随机字符串

7位到16位

什么时候使用索引

image

webpack学习

webpack

​ webpack 用于编译 JavaScript 模块。ES2015 中的 importexport 语句已经被标准化。虽然大多数浏览器还无法支持它们,但是 webpack 却能够提供开箱即用般的支持。

​ 事实上,webpack 在幕后会将代码“转译”,以便旧版本浏览器可以执行。

​ 在 webpack v4 中,可以无须任何配置,然而大多数项目会需要很复杂的设置,这就是为什么 webpack 仍然要支持 配置文件 -- webpack.config.js,如果 webpack.config.js 存在,则 webpack 命令将默认选择使用它。npx webpack --config [配置文件名]。

​ 编译不同的语言和框架就需要有不同的loader。这些loader的配置就需要在配置文件中进行配置。模块 loader 可以链式调用。链中的每个 loader 都将对资源进行转换。链会逆序执行。第一个 loader 将其结果(被转换后的资源)传递给下一个 loader,依此类推。最后,webpack 期望链中的最后的 loader 返回 JavaScript。

module: {
    rules: [
      {
        test: /\.css$/i,
        use: ['style-loader', 'css-loader'],
      },
    ],
  },
 };

​ 应保证 loader 的先后顺序:'style-loader' 在前,而 'css-loader' 在后。如果不遵守此约定,webpack 可能会抛出错误。

管理输出

​ 多入口的配置:

const path = require('path');
 
 module.exports = {
  entry: './src/index.js',
  entry: {
    index: './src/index.js',
    print: './src/print.js',
  },
   output: {
    filename: 'bundle.js',
    filename: '[name].bundle.js',
     path: path.resolve(__dirname, 'dist'),
   },
 };

插件

​ 自动生成index.html

// dash
npm install --save-dev html-webpack-plugin

// wepack.config.js
plugins: [
  new HtmlWebpackPlugin({
    title: '管理输出',
  }),
],

开发环境相关配置

sourcemap

webpack打包之后,可能无法追踪到代码出错的位置,所以会需要sourcemap

集成typescript

安装ts-loader

没看懂的部分

https://webpack.docschina.org/guides/code-splitting/

https://webpack.docschina.org/guides/build-performance/#general

react学习记录2

image
image

reactDOM的使用,可以直接写html

可以直接定义jsx变量

image

默认配置props

image

小练习

需求:点击按钮实现state和props的变化

image

react循环出数组

image
image

使用map方便

有关props还不是很懂

子组件怎么想父组件传递数据

image

不能直接赋值给props

个人简历

何文鑫-男-3年工作经验

​ 手机号:18178294391

​ 邮箱:[email protected]

​ 毕业院校:合肥工业大学-软件工程学院(211工程985工程优势学科创新平台

职业技能

  1. 精通 html,css,js(es6)
  2. 精通 react,react-hooks,redux,react-router
  3. 使用taro框架开发企业级商城小程序,精通微信小程序开发及优化
  4. 熟悉vue,vuex,vue-router
  5. 了解前端性能优化,webpack配置

工作经历

​ 2018-6 至 2018-10 上海爱用宝科技有限公司

​ 2018-10 至 2019-2 福建创昱达科技有限公司

​ 2019-3 至今 再惠(上海)网络科技有限公司

项目经验

  1. 面向用户的商城类小程序

    项目背景:使用taro和mobx进行开发,迭代版本接近20个,累计使用人数近2000人,交易金额近千万,微信小程序搜索惠吖咪可查看。

    我的工作:从零开始参与构建,版本1.1开始担任主程,负责版本迭代接近20个,负责期间能按时根据产品经理prd,设计原型编写代码,实现产品。能根据小程序后台日志,接口报警,sentry日志,及时修复线上问题,保证项目运行正常。迭代之余,积极进行各种项目优化,从taro2.0升级至taro3.0,开发者工具运行响应时间加速至少50%。

  2. 面向内部人员的商城管理系统

    项目背景:使用react,react-router,redux进行开发,迭代版本接近20个,为惠吖咪小程序提供配置,管理,查看订单,整理数据等功能。

    我的工作:从零开始参与构建,1.0版本就是项目主程,负责期间能按时根据产品prd,原型图实现产品。积极与业务人员进行沟通,优化产品逻辑,减少业务操作时间。积极参与项目优化,antd升级4.0,接入公司主应用,页面级埋点。

  3. 面向内部人员的保险行业管理系统

    项目背景:基于 vue 开发,ui 框架使用 elementui,满足天安佰盈保险公司全国地市级公司及总公司共 1 万多名业务员进行投保单,保单,对账单录入,审核,以及业务员入职,佣金等管理。

    我的工作:负责对账单录入,审核、佣金率配置,离职单管理等模块开发,封装公用组件可复用模版等,开发过程中不仅完成自身工作,还帮助有困难的组员,完成其落下的工作,进入维护阶段后作为主程独自进行项目维护、重构,新增需求等工作。

  4. 保险行业公司主页

    项目背景:天安佰盈保险公司主页,使用jquery,fullpage.js,particles.js,进行开发,独立完成项目,地址:http://www.ta-by.com/

    我的工作:此项目为本人独立开发,根据产品需求和原型图制作页面和粒子效果。

有关于我

热爱工作,主动要求承担工作。乐于沟通,能根据产品提出需求进行工时、实现方式等评估。业余时间乐于学习前端知识,简陋的博客,主要内容为平时的一些学习笔记:https://hkanyeeee.github.io/#/。比较自律,喜欢健身。

Ts 学习

Ts 学习

​ TypeScript提供JavaScript的所有功能,并在这些功能的基础上附加一层:TypeScript的类型系统

interface

​ 在 TypeScript 中,我们使用接口(Interfaces)来定义对象的类型

任意属性

​ 有时候我们希望一个接口允许有任意的属性,可以使用如下方式:

interface Person {
  name: string;
  age?: number;
  [propName: string]: any; // 也可以使用并集 string | number
}

​ 使用 [propName: string] 定义了任意属性取 string 类型的值。

数组

​ 首先,使用元素的类型,后跟[]表示该元素类型的数组

let list: number[] = [1, 2, 3];

​ 第二种方法使用通用数组类型Array<elemType>

let list: Array<number> = [1, 2, 3];

unknown

​ 我们可能需要描述编写应用程序时不知道的变量类型。这些值可能来自动态内容(例如,来自用户),或者我们可能希望有意接受API中的所有值。在这些情况下,我们希望提供一种类型,该类型告诉编译器和将来的读者该变量可以是任何变量,因此我们将其赋予unknown类型

let notSure: unknown = 4;

notSure = "maybe a string instead"; // OK, definitely a boolean

notSure = false;

never

​ never 类型表示的是那些永不存在的值的类型。 例如, never 类型是那些总是会抛出异常或根本就不会有返回值的函数表达式或箭头函数表达式的返回值类型; 变量也可能是 never 类型,当它们被永不为真的类型保护所约束时。

​ 官方文档中举了两个函数例子第一个函数是会抛出异常的函数,因此永远不会正常返回值,所以可以定义该函数的返回值为 never;第二个函数是一个 while(true){} 的函数,因此会陷入永久循环,也不会正常返回值,所以可以定义该函数的返回值为 never。这两个函数都是永远不会到达终点

只读属性

​ 某些属性仅在首次创建对象时才可以修改。您可以通过将readonly属性名称放在前面来指定此名称:

interface Point {
  readonly x: number;
  readonly y: number;
}
let p1: Point = { x: 10, y: 20 };
p1.x = 5; // error!

​ 注意,不应该使用大写开头的类型声明

函数类型

​ 接口能够描述JavaScript对象可以采用的各种形状。除了使用属性描述对象外,接口还可以描述函数类型。

​ 为了描述带有接口的函数类型,我们给接口一个调用签名。这就像只声明参数列表和返回类型的函数声明。参数列表中的每个参数都需要名称和类型。

interface SearchFunc {
  (source: string, subString: string): boolean;
}

​ 定义后,我们可以像使用其他接口一样使用此函数类型接口。在这里,我们展示了如何创建函数类型的变量并为其分配相同类型的函数值。

let mySearch: SearchFunc;

mySearch = function (source: string, subString: string) {
  let result = source.search(subString);
  return result > -1;
};

函数剩余参数的类型

function buildName(firstName: string, ...restOfName: string[]) {
  return firstName + " " + restOfName.join(" ");
}

// employeeName will be "Joseph Samuel Lucas MacKinzie"
let employeeName = buildName("Joseph", "Samuel", "Lucas", "MacKinzie");

泛型

泛型是一种以类似形参的方式传入基本类型从而实现定义类型的特殊类型标识符

function identity<T>(arg: T): T {
  return arg;
}
let output = identity<string>("myString");
//       ^ = let output: string
type Tree<T> = {
  value: T;
  left?: Tree<T>;
  right?: Tree<T>;
};

ts自带的全局类型

// 返回T的部分类型
type Partial<T> = {
  [P in keyof T]?: T[P];
};

// Required<Type>返回T的全部类型
// Property 'b' is missing in type '{ a: number; }' but required in type 'Required<Props>'.
const obj2: Required<Props> = { a: 5 };

// 属性都为只读,所以对应的对象属性都不能进行修改
type Readonly<T> = {
  readonly [P in keyof T]: T[P];
};

// Record<Keys,Type>构造一个对象类型,其属性键为Keys,属性值为Type。该实用程序可用于将一个类型的属性映射到另一个类型。
interface PageInfo {
  title: string;
}

type Page = "home" | "about" | "contact";

const nav: Record<Page, PageInfo> = {
  about: { title: "about" },
  contact: { title: "contact" },
  home: { title: "home" },
};

nav.about;
// ^ = const nav: Record

// Pick<Type, Keys>通过Keys从中选择属性集来构造类型Type
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Pick<Todo, "title" | "completed">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

// Omit<Type, Keys>与Pick对应,构建除了keys以外的对象
interface Todo {
  title: string;
  description: string;
  completed: boolean;
}

type TodoPreview = Omit<Todo, "description">;

const todo: TodoPreview = {
  title: "Clean room",
  completed: false,
};

// Exclude<Type, ExcludedUnion>从type中排除ExcludedUnion
type T0 = Exclude<"a" | "b" | "c", "a">;
//    ^ = type T0 = "b" | "c"
type T1 = Exclude<"a" | "b" | "c", "a" | "b">;
//    ^ = type T1 = "c"
type T2 = Exclude<string | number | (() => void), Function>;
//    ^ = type T2 = string | number

// Extract<Type, Union>与exclude相反,取并集
type T0 = Extract<"a" | "b" | "c", "a" | "f">;
//    ^ = type T0 = "a"
type T1 = Extract<string | number | (() => void), Function>;
//    ^ = type T1 = () => void

// NonNullable<Type>排除空type
type T0 = NonNullable<string | number | undefined>;
//    ^ = type T0 = string | number

// Parameters<Type>type为一个函数,从函数类型的参数中使用的类型构造一个元组类型Type
type T1 = Parameters<(s: string) => void>;
//    ^ = type T1 = [s: string]

// ConstructorParameters<Type>与Parameters类似,提取构造函数的类型
type T0 = ConstructorParameters<ErrorConstructor>;
//    ^ = type T0 = [message?: string]

// ReturnType<Type>与Parameters类似,提取函数返回值的类型
type T0 = ReturnType<() => string>;
//    ^ = type T0 = string

三斜线指令和.d.ts

​ 习惯上,常常把外部声明写在一个后缀名为 .d.ts 的声明文件中,然后用三斜线指令引入进来

// jquery.d.ts 文件
declare let $: (selector: string) => {
  html: (content: string) => void;
};

// main.ts 文件
/// <reference path="./jquery.d.ts" />
$('body').html('hello world');

​ 上述语句声明了 main.ts 依赖 jquery.d.ts 声明文件,在编译阶段,被依赖文件 jquery.d.ts 将被包含进来,就像将被依赖文件的源码展开在依赖声明处一样:

// main.ts文件等价于将代码在三斜线指令处展开
declare let $: (selector: string) => {
  html: (content: string) => void;
};
$('body').html('hello world');

​ 三斜线指令中需要注意的是 path 类型和 types 类型的区别:

/// <reference path="./jquery.d.ts" />
/// <reference types="node" />
  • path 类型声明的是对本地文件的依赖,包含路径信息
  • types 类型声明的是对 node_modules/@types 文件夹下的类型的依赖,不包含路径信息

react学习记录3

一个小时钟

image

父子组件的识别,父包含子,所以是父render中含有子组件,然后父的state传到子组件的props中

this.props.children的用法

如果是使用通常的props来进行渲染,则需要在reactDOM标签里写,如下

image

如果是使用children的话则只需要在ReactDOM中编写普通的html代码。然后render中写this.props.children就可以使用

localstorage是在客户端中不变的数据

image

这样写react样式

image
image

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.