随笔(一)

最近在看面试八股文,有点无聊,重新回顾了一下生疏的知识点。

一. websocket

1. 基本介绍与对比

一种基于TCP的应用层协议,浏览器与服务器只需一次握手即可持久连接,进行全双工通讯。

其实就是客户端通知服务器哪些ID的客户端可以处理事件。

特点:服务器可以向客户端主动推动消息,客户端也可以主动向服务器推送消息。

缺点:在服务器端配置比较复杂。

对比:短轮询:每隔一段时间就发送请求,浪费资源;长轮询:服务端收到请求,挂起;SSE:使用流信息让服务器向客户端推送信息。

2. 多tab页通信

前端现在常常流行SPA,页面切换基于vue-router,如果逻辑复杂,处理会很麻烦,所以有的功能会利用MPA,那就存在浏览器多tab页通信问题,解决方案如下:

  • localStorage:同源共享策略,将需要共享的信息通过setStorage(name, val)的方式存储,获取只需监听store事件即可。【同源共享,但存储大小有限制】
  • websocket:websocket最大的特点就是全双工通信,利用此特点,建立第三方服务器,让每个tab页成为客户端,建立通信。当一个tab页进行信息更新,直接send即可,接着让服务器进行共享信息广播(除进行更新的tab页)。【推荐用于跨域共享】
  • shareWorker:由于js是单线程的,针对此出现了webWorker,而shareWorker就是其中的一种。机制与websocket一致,但是是非跨域。某个tab页通过postMessage更新信息,其他tab页借onMessage监听共享信息。【兼容性不好】
  • cookie+setInterval:古老方法,共享信息存入cookie,但是cookie没有store的监听机制,需要通过如setInterval这种方法模拟轮询机制,进行信息广播。【存储大小有限制,消耗性能】

3. 跨域

  • JSONP:利用script标签不受跨域限制,将带有callback参数的GET请求嵌入。【只支持GET请求】

  • CORS:跨域资源共享机制。实施需要服务器进行相关配置,后端设置响应头Access-Control-Allow-Origin(表示哪些域名可以访问资源)。

    浏览器访问需要判断是简单请求还是非简单请求,条件是:

    • GET/POST/HEAD
    • Accept、Accept-Language、Content-Language、Last-Event-ID、Content-Type(application/x-www-form-urlencoded、multipart/form-data、text/plain)

    非简单请求发送之前需要发送预检请求(OPTIONS)进行嗅探,询问服务器可访问范围。

  • 代理转发

    原理:同源策略只是浏览器对安全的限制,而服务器之间没有同源策略。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    export default defineConfig(({command, mode}) => {
    const env = loadEnv(mode, process.cwd(), '')
    return {
    plugins: [
    vue()
    ],
    server: {
    host: '0.0.0.0',
    port: env.VITE_APP_PORT,
    proxy: {
    '/api': {
    target: env.VITE_APP_API_BASEURL,
    changeOrigin: true,
    rewrite: (path) => path.replace(/^\/api/, ''),
    },
    }
    }
    }
    });

    本地开发这里就用到代理,浏览器通过配置server,将实际请求转到本地服务,再转发到后端地址。

    最终代码打包会通过如nginx来实现。

  • postMessage

    H5新增的跨文档消息传递机制

  • websocket

二、webpack

首先得了解页面渲染过程,首先源代码parse AST,接着使用diff对AST进行操作,最后将修改后的AST generate。这里涉及到JS语言是解释性的原因,C/Java等都是直接转成二进制文件进行运行。

如今都是将AST转成字节码进行编译,但是以前都是直接通过解释器对AST进行展示。

所以可以在AST—>结果之间加入工具(多一个步骤进行操作)

1. Babel

是source to source的转换,常用功能如ES6转ES5、缺失特性处理、源码转换、taro编译等。

分为三个阶段:

  • parse:源代码—>AST
  • transform:遍历AST,使用visitor访问节点,进行增删改,返回新的AST
  • generate:AST—>目标代码字符串,同时生成sourcemap(源码到目标代码的转换关系),这玩意用来调试

AST的公共属性有:type、start、end(源码起始位置)、loc、leadingComments、innerComments、trailingComments、extra(额外信息)

API:parser、traverse、visitors、generator(含sourcemaps:true)、code-frame(标记代码位置)

babel-auto-tracker:埋点,可以监控函数执行效率。

polyfill:Babel虽然说是对ES6进行5的转化,但这只是语法层面,api层面需要ployfill。

2. webpack

使用原因:执行效率,不打包基础支持不够。

打包方式:

  1. 把js、css、image看作一个模块,用import/require引入
  2. 找到entry,通过entry找到依赖文件,将其打包在一块
  3. 将最终的bundle文件拆分,异步按需加载
  4. 一个文件多处引用,也仅打包为一个文件
  5. 多个entry中如果有相同代码,可以用插件抽离

具体过程描述一下:首先通过入口文件创建chunk,默认名字叫main;接着进入模块文件(根据entry地址),逐一转换文件代码,具体过程为:chunk中创建模块记录表(模块id也就是文件绝对路径+转换后的代码),每次转换前看一下表有没有转换过,如果没有,就读取文件内容,语法分析形成AST,记录依赖保存到dependencies中,替换依赖函数,将转换的代码放入模块记录表里面。

  • loader

    由于webpack只支持JS和JSON,可以通过loader去支持其他文件类型并进行转换。

    loader本质是一个函数,接收源文件作为参数,返回转换的结果。

    名称 描述
    babel-loader 转换ES6、ES7等新语法
    style-loader 将css通过style标签放进html的head里
    css-loader 支持.css文件加载解析
    less-loader 将less转换成css
    ts-loader 将ts转换成js
    file-loader 进行图片、字体的打包
    raw-loader 将文件以字符串导入
    thread-loader 多进程打包JS、CSS,与happypack一样

    加载解析less

    1
    2
    3
    4
    5
    6
    7
    8
    module: {
    rules: [
    {
    test: /\.less$/,
    use: ['style-loader', 'css-loader', 'less-loader'],
    },
    ]
    }

    解析ES6

    1
    2
    3
    4
    5
    6
    7
    8
    9
    module: {
    rules: [
    {
    test: /\.js$/,
    use: 'babel-loader',
    exclude: /node_modules/ //不在这个文件夹中寻找
    },
    ]
    }
  • 版本区别

    在webpack5之前,读取文件需要借助raw-loader、url-loader、file-loader。5就直接通过资源类型模块(asset module type)来替换。

    5 5之前 作用
    assert/resource file-loader 将文件发送到输出目录
    assert/inline url-loader 导出资源data url
    assert/source raw-loader 导出文件源代码
    assert

    举例子说一下吧,比如图片加载,我们推荐外部引用使用link,内部直接资源内联即可。这种切换标准可以在assert中设置图片大小maxsize,如果小于maxsize,直接将图片转为base64进行嵌入img src中。

  • 插件

    作用:用于bundle文件优化,资源管理和环境变量注入,作用在整个构建过程。

    常用插件 作用
    HtmlWebpackPlugin 将输出的css/js等套入html模板内
    CleanWebpackPlugin 清理dist目录
    ExtractTextWebpackPlugin 提取css
    uglifyjsWebpackPlugin 压缩JS
    CommonsChunkPlugin build之后js文件夹里面那些公共js
  • mode

    有三种:development、production、none

3. 文件监听

文件监听:源码发生变化,自动重新构建出新的输出文件。

webpack开启监听方法:

  1. 启动webpack,带上–watch,本质就是轮询,所以不建议采用【每次要手动刷新浏览器】

  2. 配置webpack.config.js中设置watch:true

    此种方法就是HMR热更新,目的是做到不用刷新浏览器而将新变更的模块替换旧模块。

    1
    2
    3
    4
    5
    6
    module.exports = {
    ...
    devServer: {
    hot: true,
    }
    }

    npm run server

    runtime可以理解为js运行环境

    这里可能涉及到文件指纹,就是文件名加个base后缀,判断文件是否有改动。

4. 代码压缩

针对的主要就是html、css、js文件进行压缩。

插件 使用
Html-minimzer-webpack-plugin new HtmlMinimzerPlugin()
css-minimzer-webpack-plugin new CssMinimzerPlugin()
terser-webpack-plugin new TerserPlugin()

5. 小程序适配

这些插件的使用让我想起之前做学校答题小程序多端适配问题,网上的解决方案有什么用em/rem,这种开发的时候贼麻烦,其实还是可以使用px完成本地开发,然后用px2rem-loader和lib-flexible(淘宝的库)实现多设备同步。

但是会遇到一个问题,lib-flexible是个js文件,需要引入,但是webpack打包的时候,script标签是在最后面的,这样无法自动转换(如font-size),影响渲染效果。

解决方案:使用资源内联方法

1
2
3
<script>
<%= require('raw-loader!babel-loader!../node_moudles/amfe-flexible/index.min.js').default %>
</script>

raw-loader以字符串导入文件,babel-loader针对ES6的语法兼容问题。

6. 项目打包

SPA页面渲染是生成AST,在框架加载完毕,才将html/css等样式渲染上去,不利于搜索引擎抓包,为此出现了SSR(vue用的是Nuxt.js)等概念,都是为了SEO优化。

针对MPA打包,原理就是通过遍历入口文件(Object.keys(entryFiles)),对每个页面使用HtmlWebpackPlugin将各页面样式套入模板内,导出入口、数组(存放加载完的页面)。

DLL的目的是提前对基础库进行打包,加快build速度,但是实际情况vue-cli等都是关闭的,因为dll需要提前配置,不一定效益成正比。

这里就需要提到一个问题,我之前的管理平台为啥使用vite,而不是webpack进行打包。vite内部借助Esbuild来提升性能(Esbuild是用go开发,多线程打包,代码直接变为机器码),而webpack打包过程中存在众多resolve、loader、parse、transform等,都需要js来操作,而js本身就是解释性语言,需要编译,调试还是单线程。

1
2
const res = await esbuild.tansform(code, options)    //将code转化为指定格式的内容
esbuild.build(options) //打包构建

vite利用Esbuild来进行预构建和内容转换,其中预构建目的是 将非ESM规范代码转换、将第三方依赖内部的多个文件合并为一个,减少http请求数量。

这个收集第三方依赖的过程:解析模块的路径,触发onResolve hook,判断是第三方依赖还是业务代码,最终所有文件都在 /node_modules/.vite/deps 中。

vite本质和webpack差不多,第一步createAssets收集和处理文件代码,vite借助esbuild,而webpack里面借助Babel;第二步createGraph创建文件依赖图;第三步bundle,输出bundle.js文件。

三、项目规范

1. 项目规范

项目的规范还要有测试(单元测试、冒烟测试等)、git规范、changelog文档

git常用命令如下:

1
2
3
4
5
6
7
git pull
git status
git add .
git commit -m '注释'
git push
git branch -b xxx
git merge 'xxx'

解决冲突问题:

  1. 两分支修改同一文件:

    当前分支直接修改代码,然后提交即可

  2. 两分支修改了同一个文件名字:

    本地当前分支上,修改冲突代码,push(记得要沟通)

四、项目优化

1. vue性能优化

  • 编码角度

    不要把数据都放在data中;

    keep-alive缓存组件;

    尽可能拆分组件,提高复用率;

    key值唯一;

    路由懒加载;

    数据持久化存储尽量用防抖、节流;

  • 加载优化

    按需加载;

    内容懒加载;

    图片懒加载;

  • 用户体验

    骨架屏;

  • SEO优化

    预渲染;

    服务端ssr;

  • 打包优化

    多线程打包(vite);

    CDN形式加载第三方模块;

    抽离公共文件;

2. 首屏优化

  1. 使用路由懒加载;
  2. 非首屏组件使用异步组件;
  3. 首屏不重要的组件延迟加载;
  4. 减少首屏js\css文件大小;
  5. 尽量减少DOM的数量和层级;
  6. 精灵图;
  7. 图片懒加载;

3. vue3优化

vue3为啥性能比2好,讨论前首先需知道两者区别。

  • 2 VS 3

    2数据劫持用的是object.defineProperty,3用的是proxy,实现对所有对象的监听,无需对数组重写方法;

    3支持framegrament,可以有多个根节点;

    2用的是选项式api,3是组合式api;

    生命周期不同,3直接setup;

    2可以用原型,3是工厂函数;

  • 性能优势

    diff算法的优化(加了静态标记);

    静态提升(对没有变化的元素只渲染一次,后面复用);

    事件监听缓存;