《webpack&打包构建&项目优化》前端面试题

Monorepo + pnpm实现依赖共享

Monorepo 是将多个包(package)统一放置在一个仓库(repo)进行管理的方式。这种方式可以更好地管理包之间的依赖与兼容,统一的提交日志也更容易追踪问题。

在 Monorepo 项目中使用 pnpm 具有以下优势:

  1. 空间节省:通过硬链接的方式,无需重复安装包,这在有许多包的 Monorepo 项目中特别有效。
  2. 依赖共享:不同包可以共享依赖,简化依赖管理,避免重复依赖的安装与升级。
  3. 统一依赖:pnpm 以锁定文件(pnpm-lock.yaml)来管理依赖版本,这可以促进 Monorepo 中的包采用同一版本的依赖。
  4. 快速安装:pnpm 通过硬链接,只需安装一次包,后续的安装会非常快速,这很适合 Monorepo 频繁开发与构建的场景。

webpack的模块联邦是什么

Webpack Module Federation(模块联邦)是 Webpack 5 引入的一个功能,旨在解决现代前端应用程序中微服务架构下的模块共享和跨团队协作的问题。它允许不同的独立构建的应用程序在运行时动态加载和共享模块。

具体来说,Webpack Module Federation 提供了以下主要功能和优势:

  1. 动态远程模块加载:应用程序可以在运行时动态加载来自其他应用或服务的模块。这使得将一个大型应用程序拆分为更小的、可独立部署的微前端更加容易。

  2. 共享模块:多个应用程序可以共享相同的代码和依赖,而不需要复制和维护多份副本。这减少了代码重复,提高了开发效率和维护性。

  3. 版本管理:Webpack Module Federation 提供了一种机制来处理模块的版本兼容性和管理,确保各个应用程序使用的模块版本是兼容的。

  4. 隔离性:虽然模块是共享的,但每个应用程序仍然可以保持独立性,避免模块之间的命名冲突和全局状态污染。

  5. 灵活性和扩展性:开发团队可以更灵活地组织和扩展应用程序,按需引入和使用外部模块,而无需事先硬编码这些依赖关系。

Webpack Module Federation 的核心概念是远程模块加载和共享,通过这种方式,前端团队可以更好地管理复杂的应用程序架构,实现模块化、高效的团队协作和开发。

Polyfill和babel区别

Babel

  1. 作用:Babel是一个JavaScript编译器,它的主要功能是将ES6+代码转换为ES5代码,以确保在不支持ES6+特性的旧版浏览器中能够正常运行。

  2. 实现方式:Babel通过语法转换来实现兼容性。例如,将箭头函数、类、模块、解构赋值等新特性转换为等价的ES5代码。

  3. 应用场景:适用于需要使用现代JavaScript语法但必须兼容旧浏览器的项目。

  4. 配置:Babel可以通过配置文件(如.babelrc或babel.config.js)进行高度定制,包含不同的插件和预设来处理各种JavaScript语法和特性。

Polyfill

  1. 作用:Polyfill是一段代码(通常是库),它在环境中添加缺失的API或功能使得旧浏览器能够支持新的Web标准API。例如,添加对PromisefetchArray.prototype.includes等现代API的支持。

  2. 实现方式:Polyfill通过检测某个功能是否存在,如果不存在,则定义该功能。例如,如果浏览器不支持Promise,Polyfill会提供一个实现方案,使开发者可以使用Promise

  3. 应用场景:适用于需要使用现代Web API,但必须确保在旧浏览器中也能使用这些API的项目。

  4. 加载方式:Polyfills通常作为外部库引入,可以在HTML中通过<script>标签引入,或者通过npm/yarn等包管理工具安装并在代码中导入。

webpack 中的loader 和plugin的区别,编写 他们的思路

Loader【转换器/加载器】:

作用: Loader 用于在模块加载时对文件进行转换。它是一个转换器/加载器,将文件从一种形式转换为另一种形式,例如,将 ES6 语法的 JavaScript 文件转换为能够在浏览器中运行的普通 JavaScript【babel-loader】、css-loader、file-loader、style-loader等
使用场景: Loader 通常被配置在 module.rules 中,指定了哪些文件应该被哪些Loader处理。每个 Loader 执行一个特定的转换操作,它们串联在一起形成一个转换管道。

编写loader: loader 的本质是函数,输入一个类型的文件,输出另一个类型的文件,把输入的文件,使用正则表达式等方式,进行语法解析,生成AST抽象语法树,然后转换成目标文件。功能要保持单一,比如 less 文件转换成文件是需要链式调用多个loader才实现的,分别是 less-loader 、css-loader 、style-loader。
loader 按照配置的顺序,从右往左/有下往上执行,先调用 less-laoder,然后是 css-loader,最后是 style-loader
最后一个loader 一定是转换成 js代码模块【包含 export.module=】,这样才能在 js 中导入使用
自定义的 loader 插件代码如下,实现了一个 txt-loader
 
const fs = require('fs')
module.exports = function (source) {
    // resourcePath 是上下文中的变量
    const txtContent = fs.readFileSync(this.resourcePath, 'utf-8')
    return `module.exports = ${JSON.stringify(txtContent)}`
}
Plugin【插件】:

作用: Plugin 用于执行范围更广的任务,它可以监听 Webpack 构建过程中的事件,执行一些额外的任务。Plugins可以用于优化、压缩、拷贝文件等各种操作,而不仅仅是文件转换
使用场景: Plugins 通过在 Webpack 配置中的 plugins 数组中添加实例来配置。一个插件通常包含一个或多个钩子函数,这些函数在 Webpack 构建的不同阶段被调用。

编写plugin: 我们在使用 plugin 的时候一般都用 new Plugin 这种语法,插件必须是一个函数或一个包含 apply 方法的类,才能访问 compiler 实例;因为, webpack 打包时会调用 plugin 的 apply方式执行 plugin的逻辑,同时给apply 方法传递一个 compiler 参数。每个插件 compiler 和 compilation 对象都是同一个引用;异步的事件需要在插件处理完成时调用回调函数通知 webpack 进入下一个流程,不然会卡住。
compiler 是webpack 编译会创建的核心对象,包含 webpack 环境的所有配置信息;
compilation 是 plugin 内置的事件回调函数的参数,包含了当前的模块资源、编译生成资源、变化的文件以及被跟踪依赖的状态信息;
webpack 基于发布订阅模式,插件监听这些事件,在特定的阶段执行自己的任务。
apply 方法执行时,可以操作webpack 本次打包的各个生命周期 hooks,所以插件也就可以在不同的时间节点做一些操作,而这都依赖 webpack 给 apply 方法传递的那个参数 comilper 而实现。
自定义的 plugin 代码如下,在 done 的hooks里面打印了一些信息
class MyWebpackPlugin {
    constructor({ message }) {
        this.message = message
    }
    apply(compiler) {
        // compiler.hooks 里面有各种钩子
        compiler.hooks.done.tap('MyWebpackPlugin', () => {
            console.log('compiler.hooks.done', this.message)
        })
    }
}
module.exports = MyWebpackPlugin
总体而言,Loader主要用于文件的转换和文件加载,而 Plugin 则用于执行更广泛的构建任务,如文件生成、代码优化等。它们共同组成了 Webpack强大的构建工具生态系统。

webpack的生命周期

初始化阶段(Initialization):
environment: 在创建 webpack 实例的时候触发,用来读取 webpack 配置和设置环境。
entry-option: 在设置入口点(entry)和选项(options)之后触发,但在开始读取模块和加载插件之前。
编译阶段(Compilation):

before-run: 在开始编译之前触发,异步钩子【CleanWebpackPlugin】。
run: 在读取记录和模块之前触发,异步钩子。
normal-module-factory: 创建模块之前触发。
context-module-factory: 创建模块上下文之前触发。
before-compile: 编译器开始编译之前触发。
compile: 编译器开始编译时触发。
构建阶段(Build):

this-compilation: 编译器开始重新编译时触发。
compilation: 编译器创建新的编译实例时触发【DefinePlugin,HotModuleReplacementPlugin,ProvidePlugin】。
make: 当编译创建开始之前触发。
after-compile: 编译完成时触发。
优化阶段(Optimization):

optimize: 在执行优化(optimization)和插件应用之前触发。【OptimizeCSSAssetsPlugin,terser-webpack-plugin,css-minimizer-webpack-plugin,uglifyjs-webpack-plugin】
optimize-modules: 在模块优化之前触发。【OptimizeModulesPlugin】
optimize-chunks: 在块优化之前触发。【OptimizeChunksPlugin】
optimize-chunk-assets: 在块资产优化之前触发。【OptimizeChunkAssetsPlugin】
产出阶段(Output):

before-emit: 在生成资源到 output 目录之前触发。
emit: 在生成资源到 output 目录之后触发。【MiniCssExtractPlugin,HtmlWebpackPlugin,inline-chunk-html-plugin,CopyWebpackPlugin】
after-emit: 在生成资源到 output 目录之后触发。
完成阶段(Completion):

done: 编译完成后触发,包括成功和失败的情况【webpack-bundle-analyzer】。
failed: 编译失败时触发。
invalid: 监视模式下,编译无效时触发。
watch-run: 在监视模式下,在每次重新编译之前触发。
你会发现,常用的插件的生命周期一般都是 optimize 优化和 emit,所以这两个你怎么都得记住了吧

path和publicpath区别

  1. path

    • path是用来指定输出文件的实际路径。它告诉Webpack将打包好的文件输出到哪个目录下。通常,你会将输出目录设置为项目的发布目录(例如dist目录),这样构建完成后,所有生成的文件都会放在这个目录下。
  2. publicPath

    • publicPath用于指定在浏览器中访问打包生成的文件时,引用这些文件的基础URL。它通常用于在构建多页面应用或将静态资源部署到CDN时设置静态资源的路径。

总结来说,path用于指定本地文件系统中输出文件的目录,而publicPath用于指定在浏览器中访问这些文件时的基础URL路径。在许多情况下,它们可以一起使用来正确配置输出文件的路径和公共资源的访问路径。

webpack4和5的区别

  1. Tree Shaking优化

    • 在Webpack 4中,尽管已经支持Tree Shaking,但开发者可能需要手动配置optimization.usedExports或者使用sideEffects标志来帮助Webpack更好地识别无用模块。
    • Webpack 5对于ES模块的Tree Shaking处理更为精细,usedExports默认启用,同时sideEffects的检查也更加严格,通常不需要显式设置usedExports
  2. 持久化缓存机制

    • Webpack 4实现缓存通常需要借助第三方插件,如hard-source-webpack-plugin
    • Webpack 5则内置了持久化缓存机制,提供了更好的缓存策略。
  3. Hash生成方式改变

    • Webpack 4是根据代码的结构生成chunkhash,添加了空白行或注释,会引起chunkhash的变化。
    • Webpack 5是根据内容生成chunkhash,改了注释或者变量不会引起chunkhash的变化,浏览器可以继续使用缓存。
  4. 优化了对缓存的使用效率

    • 在Webpack 4中,chunkId与moduleId都是自增id,新增模块或入口会导致文件内容变化,从而影响缓存效率。
    • Webpack 5采用新的算法来计算确定性的chunkId和moduleId,可以有效利用缓存,在production模式下,optimization.chunkIdsoptimization.moduleIds默认会设为deterministic,有益于长期缓存。

dependencies devDependencies peerDependencies三者的区别

  1. dependencies: 这是项目在运行时所需的依赖项。换句话说,这些是必须在应用程序运行时加载的包。例如,如果你的应用程序依赖于Express框架,则Express将是一个dependencies

  2. devDependencies: 这是仅在开发时需要的依赖项。这些通常是测试框架、构建工具、代码风格检查器等。这些依赖项不会被包含在最终的生产构建中。例如,如果你使用Jest进行测试,那么Jest将是一个devDependencies

  3. peerDependencies: 这是指您的模块所依赖的其他模块。这个概念用于创建库或插件,其中您需要指定您的模块需要依赖于使用者的其他模块版本。它指定了您的包依赖于其他包的特定版本,这使得在npm或Yarn安装您的包时,它会提醒用户安装特定版本的peer依赖项。

总结一下:

  • dependencies 用于生产环境,包含运行应用程序所需的依赖项。
  • devDependencies 用于开发环境,包含仅在开发过程中需要的依赖项。
  • peerDependencies 用于指定您的模块需要依赖于使用者的其他模块版本

webpack的构建原理

  1. 入口(Entry): Webpack 从一个或多个入口文件开始构建依赖图。入口文件可以是 JavaScript 文件,但也可以是其他类型的文件,比如 CSS 或图片文件。Webpack 根据入口文件来确定应用程序的依赖关系。

  2. 依赖解析(Dependency Resolution): Webpack 解析入口文件及其依赖的模块,并生成模块之间的依赖关系图。Webpack 支持多种模块类型,包括 CommonJS、AMD、ES6 模块等。

  3. 模块转换(Module Transformation): 对于每个模块,Webpack 会根据配置的加载器(Loader)对其进行转换。加载器可以将非 JavaScript 文件转换为 JavaScript 文件,或者对 JavaScript 文件进行预处理,比如转换 ES6/ES7 语法、处理 CSS 前缀等。

  4. 代码分割(Code Splitting): Webpack 支持将代码拆分成多个块(chunk),这样可以实现按需加载(Lazy Loading),减小首次加载时间,提高应用程序的性能。Webpack 提供了多种代码分割的方式,包括通过 import() 函数动态加载模块、使用 Webpack 的内置优化插件等。

  5. 模块合并(Module Bundling): 经过加载器处理和代码分割后,Webpack 将所有模块及其依赖关系打包成一个或多个输出文件。输出文件的格式可以是普通的 JavaScript 文件、CommonJS 模块、UMD(Universal Module Definition)模块等,取决于配置中的输出选项。

  6. 优化与压缩(Optimization and Minification): 在打包过程中,Webpack 还会对输出文件进行优化和压缩,以减小文件大小、提高加载速度。Webpack 内置了多种优化功能,包括代码压缩、去除无用代码、提取公共代码等。

  7. 输出(Output): 最后,Webpack 将打包后的文件输出到指定的目录中。输出文件可以是单个 JavaScript 文件,也可以是多个文件,取决于配置中的输出选项。

前端之所有需要 类似于 Webpack 这样的构建工具,是为了提高项目的开发效率,Webpack 通过分析js中的 require 语句,分析出当前 js 文件所有的依赖文件,通过递归的方式层层分析后,得到整个项目的依赖关系图,对图中不同的文件执行不同的 loader,比如使用 css-loader 解析css代码,最后基于这个依赖关系图读取到整个项目中的所有文件代码,进行打包处理之后交给浏览器执行。

webpack插件的基本组成 

  • 一个具名 JavaScript 函数;

  • 在它的原型上定义 apply 方法;

  • 指定一个触及到 Webpack 本身的事件钩子;

  • 操作 Webpack 内部的实例特定数据;

  • 在实现功能后调用 Webpack 提供的 callback。

一个基本插件

class HelloWorldPlugin {
  apply(compiler) {
    compiler.hooks.done.tap('Hello World Plugin', (
      stats /* 在 hook 被触及时,会将 stats 作为参数传入。*/
    ) => {
      console.log('Hello World!');
    });
  }
}
module.exports = HelloWorldPlugin;

webpack与 vite 的不同, vite的速度提升在哪里

ViteWebpack快的主要原因包括其底层语言的选择、打包过程、热更新机制以及使用方面的优化。12

  1. 底层语言的选择:Webpack基于Node.js构建,其运行速度受到JavaScript毫秒级别性能的限制。相比之下,Vite基于esbuild预构建依赖,而esbuild采用Go语言编写,Go语言的性能级别达到纳秒级别,这使得Vite在处理速度上远超Webpack。

  2. 打包过程:Webpack在启动时需要分析各个模块之间的依赖关系,并进行编译打包,这个过程随着项目规模的增大而变得缓慢。而Vite启动服务器后,按需动态编译请求的模块,不需要预先打包整个项目。这种按需编译的方式大大减少了构建时间,特别是在项目复杂度和模块数量增加的情况下,Vite的优势更加明显。

  3. 热更新机制:Webpack在热更新时需要重新编译模块及其依赖的模块,而Vite通过让浏览器重新请求更新的模块来实现热更新,这种方式显著减少了热更新的时间。

  4. 使用方面的优化:Vite开箱即用,更加简单,基于浏览器ESM(ES Modules)使得HMR(Hot Module Replacement)更加优秀,达到极速的效果。虽然Webpack在灵活性、API以及插件生态方面更加丰富,但在开发效率和启动速度方面,Vite提供了更好的体验。

综上所述,Vite通过采用更高效的底层语言、优化打包过程、改进热更新机制以及简化使用方式,实现了比Webpack更快的构建和开发体验。

收起

rollup和webpack的区别?

简单的来说rollup是一个Js模块打包器,可以将小块代码编译成大块复杂的代码。现在已经有很多类库都在使用 rollup 进行打包了,比如:react, vue, preact, three.js,moment, d3 等。
优点:小巧而专注

   特性:
       rollup 所有资源放同一个地方,一次性加载,利用 tree-shake特性来  剔除未使用的代码,减少冗余
       webpack 拆分代码、按需加载  webpack2已经逐渐支持tree-shake
   rollup:
     1.打包你的 js 文件的时候如果发现你的无用变量,会将其删掉。
     2.可以将你的 js 中的代码,编译成你想要的格式
   webpack:
    1.代码拆分
    2.静态资源导入(如 js、css、图片、字体等)
    拥有如此强大的功能,所以 webpack 在进行资源打包的时候,就会产生很多冗余的代码。

项目(特别是类库)只有 js,而没有其他的静态资源文件,使用 webpack 就有点大才小用了,因为 webpack bundle 文件的体积略大,运行略慢,可读性略低。这时候 rollup就是一种不错的解决方案
结论:对于应用使用 webpack,对于类库使用 Rollup

webpack如何解析less?(less-loader、css-loader、style-loade)

主要通过less-loader、css-loader、style-loader插件作用

less-loader:用于加载.less文件,将less转化为css

css-loader:用于加载.css文件,将css转化为commonjs

style-loader: 将样式通过<style>标签插入到head中

顺序是less-loader > css-loader > style-loader

 如何利用webpack来优化前端性能?

  • 压缩代码。删除多余的代码、注释、简化代码的写法等等方式。可以利用webpack的UglifyJsPluginParallelUglifyPlugin来压缩JS文件, 利用cssnano(css-loader?minimize)来压缩css
  • 利用CDN加速。在构建过程中,将引用的静态资源路径修改为CDN上对应的路径。可以利用webpack对于output参数和各loader的publicPath参数来修改资源路径
  • 删除死代码(Tree Shaking)。将代码中永远不会走到的片段删除掉。可以通过在启动webpack时追加参数--optimize-minimize来实现
  • 提取公共代码

如何提高webpack的构建速度?

  • 多入口情况下,使用CommonsChunkPlugin来提取公共代码
  • 通过externals配置来提取常用库
  • 利用DllPluginDllReferencePlugin预编译资源模块 通过DllPlugin来对那些我们引用但是绝对不会修改的npm包来进行预编译,再通过DllReferencePlugin将预编译的模块加载进来。
  • 使用Happypack 实现多线程加速编译,它把任务分解给多个子进程去并发的执行,子进程处理完后再把结果发送给主进程。(由于 js是单线程模型,要想发挥多核 CPU 的能力,只能通过多进程去实现,而无法通过多线程实现。)
  • 使用webpack-uglify-parallel来提升uglifyPlugin的压缩速度。 原理上webpack-uglify-parallel采用了多核并行压缩来提升压缩速度
  • 使用Tree-shakingScope Hoisting来剔除多余代码

有哪些常见的Loader?他们是解决什么问题的?

  • file-loader:把文件输出到一个文件夹中,在代码中通过相对 URL 去引用输出的文件
  • url-loader:和 file-loader 类似,但是能在文件很小的情况下以 base64 的方式把文件内容注入到代码中去
  • source-map-loader:加载额外的 Source Map 文件,以方便断点调试
  • image-loader:加载并且压缩图片文件
  • babel-loader:把 ES6 转换成 ES5
  • css-loader:加载 CSS,支持模块化、压缩、文件导入等特性
  • style-loader:把 CSS 代码注入到 JavaScript 中,通过 DOM 操作去加载 CSS。
  • eslint-loader:通过 ESLint 检查 JavaScript 代码

有哪些常见的Plugin?他们是解决什么问题的?

  • define-plugin:定义环境变量
  • commons-chunk-plugin:提取公共代码
  • uglifyjs-webpack-plugin:通过UglifyES压缩ES6代码

webpack开启热更新及原理(使得应用在运行状态下,不重载刷新就能更新、增加、移除模块的机制

  • 使用 HotModuleReplacementPlugin 插件
// webpack.config.js
module.exports = {
  // ...
  plugins: [
    webpack.HotModuleReplacementPlugin(),
   // ...
  ]
}
  • 打开 webpack-dev-server 的热更新开关,
// webpack.config.js
module.exports = {
  // ...
  devServer: {
    hot: true,
    // ...
  }
}

 基本原理:webpack监听文件变化,服务端和客户端有websocket通信,服务端向客户端发送文件变化消息,客户端根据文件变化消息获取变更模块代码,进行模块代码的热替换

1. 配置开启热更新,设置entry格式和webpack-dev-server的option,使得打包的bundle里面包含HMR runtime和websocket连接的代码
2. webpack-dev-server通过express启动服务端
3. 客户端通过sockjs和服务端建立websocket长连接
4. webpack监听文件变化,文件保存触发webpack重新编译,编译后的代码保存在内存中,不在output.path中产生输出
5. 编译会生成hash值,hot-update.json(已改动模块的json),hot-update.js(已改动模块的js)
6. 通过socket向客户端发送hash值
7. 客户端对比hash值,一致在走缓存,不一致则通过ajax获取hot-update.json,json包含模块hash值,再通过jsonp请求获取hot-update.js
8. 热更新js模块,若失败,则live reload刷新浏览器代替热更新(若模块未配置热更新,则同样live reload)

 webpack中loader和plugin的区别

一、从功能作用的角度区分:

 loader从字面的意思理解,是 加载 的意思。只专注于转化文件(transform)这一个领域,完成压缩,打包,语言翻译,仅仅只是为了打包。

plugin不仅只局限在打包,资源的加载上,它的功能要更加丰富。从打包优化和压缩,到重新定义环境变量,功能强大到可以用来处理各种各样的任务。

二、从运行时机的角度区分

 1 . loader运行在打包文件之前(loader为在模块加载时的预处理文件)
 2.  plugins在整个编译周期都起作用。

 webpack打包流程

1. 生成options (将webpack.config.js和shell中的参数,合并中options对象)
2. 实例化complier对象 (webpack全局的配置对象,包含entry,output,loader,plugins等所有配置信息)
3. 实例化Compilation对象 (compiler.run方法执行,开始编译过程,生成Compilation对象)
4. 分析入口js文件,调用AST引擎(acorn)处理入口文件,生成抽象语法树AST,根据AST构建模块的所有依赖
5. 通过loader处理入口文件的所有依赖,转换为js模块,生成AST,继续遍历,构建依赖的依赖,递归,直至所有依赖分析完毕
6. 对生成的所有module进行处理,调用plugins,合并,拆分,生成chunk
7. 将chunk生成为对应bundle文件,输出到目录

前端容灾方法

前端容灾指的是当后端接口挂了,依然可以保证页面展示信息完整。我们以百度首页中新闻模块举例,当你打开百度的时候,服务端渲染好的页面出来以后,需要请求新闻接口拿到数据渲染新闻模块。你的老板告诉你,不论任何情况,必须展示新闻和广告,即使新闻接口挂了。这个时候怎么办,也就是前端容灾的范畴了。

localstorage缓冲接口数据

新闻模块的接口,每次有返回的时候,都存入localstorage中,以接口路径为key,返回数据为value,当新闻接口请求失败的时候先从localstorage中读上次成功请求时候的数据,展示出来。

备份一份静态数据到CDN

让业主方同学提供一份兜底新闻数据,存放在CDN上,新闻接口返回失败,用户localstorage中也没有数据的时候,去CDN对应地址拉取数据渲染

利用Service worker的caches API做页面接口缓存

Service worker提供了fetch事件可供监听,当页面发出请求的时候,会先过fetch方法,你可以在这里定义任何你需要的缓存策略,比如请求成功后,将结果存入caches。Service worker中,你甚至可以缓存当前页面的HTML,让网站离线运行。

通过上面这些手段,可以保证只要给用户吐出HTML就能保证页面完整性。对于让页面始终可吐出HTML这件事,那就又属于后端,运维同学所做的了。可以借助上面我介绍到的集群的方式,也可以通过CDN的方式(动态DNS)来解决。

什么是服务端渲染ssr?为什么利于SEO?

简单理解是将组件或页面通过服务器生成html字符串,再发送到浏览器,最后将静态标记"混合"为客户端上完全交互的应用程序

SSR的优势

1. 更利于SEO。(服务端渲染返回给客户端的是已经获取了异步数据并执行JavaScript脚本的最终HTML,网络爬中就可以抓取到完整页面的信息。)

2. 更利于首屏渲染(首屏的渲染是node发送过来的html字符串,并不依赖于js文件了,这就会使用户更快的看到页面的内容。尤其是针对大型单页应用,打包后文件体积比较大,普通客户端渲染加载所有所需文件时间较长,首页就会有一个很长的白屏等待时间。)

SSR的局限

  1. 服务端压力较大(本来是通过客户端完成渲染,现在统一到服务端node服务去做。尤其是高并发访问的情况,会大量占用服务端CPU资源)
  2. 开发难度大,学习成本高

 时间耗时比较

由服务端请求首屏数据,而不是客户端请求首屏数据,这是“快”的一个主要原因。服务端在内网进行请求,数据响应速度快。客户端在不同网络环境进行数据请求,且外网http请求开销大,导致时间差。

服务端渲染是先向后端服务器请求数据,然后生成完整首屏html返回给浏览器;而客户端渲染是等js代码下载、加载、解析完成后再请求数据渲染,等待的过程页面是什么都没有的,就是用户看到的白屏。就是服务端渲染不需要等待js代码下载完成并请求数据,就可以返回一个已有完整数据的首屏页面。

语义化标签的作用

1、使页面结构清晰,即使没有css。便于后期维护。

2、便于机器读取SEO,有助于爬虫抓取更多的有效信息,爬虫是依赖于标签来确定上下文和各个关键字的权重。

SEO及其优化方案

 SEO:SearchEngineOptimization 搜索引擎优化。说白了就是让浏览器知道你的网页有什么内容,从而确定网页的排名。

  • 简化代码结构,更利用搜索引擎分析抓取有用内容,所有js、css采用外联方式,图片采用css精灵,减少请求次数
  • 优化布局,把重要的内容HTML代码放在最前,所有引擎抓取HTML内容是从上到下,利用这一特点,可以让主要代码有限读取,广告等不重要的代码放在下面。
  • 每个页面只能出现一次h1标签,h2可以出现多次,H1权重很高,普通认为仅次于title,一般资讯详情也得标题、商品详情也得标题,都放在H1里
  • 图片一定要添加alt属性,title属性可选,蜘蛛不认识图片上的内容,只能通过alt属性来判断,如果是商品列表也,所有商品都加了alt和title的话,容易造成堆砌关键词,所以一般都只加alt属性
  • 图片大小声明: 如果图片大小不做定义的话,页面需要重新渲染,就会影响到速度
  • 非特殊性链接, 链接地址一定要写入herf属性,做过SEO优化的人员都知道,爬虫目前对于js的支持很差,基本无法读取里面的链接地址,所用说用click事件是绝对不允许的,特别是一些重要的导航链接

  • 网页结构呈扁平状树型,目录结构不宜过深,每个页面离首页最多点击不超过三次,过深不利于搜索引擎的抓取
  • 做好404页面,一般都会加首页链接及错误提示,并测试其返回状态码为404,用户体验友好,可以留住用户不至于直接关闭页面,蜘蛛友好,可以返回抓取其他页面
  • 重要内容不要用js输出,重要内容必须放在HTML里

如何重构一个项目?

一、原项目梳理

  1. 页面结构
  2. 项目结构
  3. 前端框架、模板
  4. 第三方库、组件、插件

二、重构方案确立

  1. 开发规范
  2. 技术选型
  3. 构建工具
  4. 开发效率
  5. 性能优化
  6. 模块化组件化
  7. 前端安全
  8. 用户体验

 一个页面上有大量的图片(大型电商网站),加载很慢,你有哪些方法优化这些图片的加载,给用户更好的体验 ?

  • 图片懒加载,在页面上的未可视区域可以添加一个滚动条事件,判断图片位置与浏览器顶端 的距离与页面的距离,如果前者小于后者,优先加载。
  • 如果为幻灯片、相册等,可以使用图片预加载技术,将当前展示图片的前一张和后一张优先 下载。
  • 如果图片为 css 图片,可以使用 CSSsprite,SVGsprite,Iconfont、Base64 等技术。 如果图片过大,可以使用特殊编码的图片,加载时会先加载一张压缩的特别厉害的缩略图, 以提高用户体验。
  • 如果图片展示区域小于图片的真实大小,则因在服务器端根据业务需要先行进行图片压缩, 图片压缩后大小与展示一致。

 Web安全及防护原理 

sql注入原理

就是通过把SQL命令插入到Web表单递交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令。

总的来说有以下几点:

1.永远不要信任用户的输入,要对用户的输入进行校验,可以通过正则表达式,或限制长度,对单引号和双"-"进行转换等。

2.永远不要使用动态拼装SQL,可以使用参数化的SQL或者直接使用存储过程进行数据查询存取。

3.永远不要使用管理员权限的数据库连接,为每个应用使用单独的权限有限的数据库连接。

4.不要把机密信息明文存放,请加密或者hash掉密码和敏感的信息。

XSS原理

Xss(cross-site scripting)攻击指的是攻击者往Web页面里插入恶意 html标签或者javascript代码。比如:攻击者在论坛中放一个看似安全的链接,骗取用户点击后,窃取cookie中的用户私密信息;或者攻击者在论坛中加一个恶意表单,

当用户提交表单的时候,却把信息传送到攻击者的服务器中,而不是用户原本以为的信任站点。

XSS防范方法

  • 首先代码里对用户输入的地方和变量都需要仔细检查长度和对”<”,”>”,”;”,”’”等字符做过滤;其次任何内容写到页面之前都必须加以encode,避免不小心把html tag 弄出来。这一个层面做好,至少可以堵住超过一半的XSS 攻击。
  • 首先,避免直接在cookie 中泄露用户隐私,例如email、密码等等。其次,通过使cookie 和系统ip 绑定来降低cookie 泄露后的危险。这样攻击者得到的cookie 没有实际价值,不可能拿来重放。
  • 如果网站不需要再浏览器端对cookie 进行操作,可以在Set-Cookie 末尾加上HttpOnly 来防止javascript 代码直接获取cookie 。
  • 尽量采用POST 而非GET 提交表单

XSS与CSRF区别

XSS是获取信息,不需要提前知道其他用户页面的代码和数据包。CSRF是代替用户完成指定的动作,需要知道其他用户页面的代码和数据包。

要完成一次CSRF攻击,受害者必须依次完成两个步骤:

  1. 登录受信任网站A,并在本地生成Cookie。

  2. 在不登出A的情况下,访问危险网站B。

CSRF的防御

  • 服务端的CSRF方式方法很多样,但总的思想都是一致的,就是在客户端页面增加伪随机数。

  • 通过验证码的方法

 常见浏览器兼容

css兼容
1. 不同浏览器的标签默认的margin和padding不同
2. css3新属性,加浏览器前缀兼容早期浏览器
3. 超链接访问过后hover样式就不出现的问题
4.css hack解决浏览器兼容性
js兼容
1.事件绑定
2. event事件对象问题
3. event.srcElement(事件源对象)问题
4. 阻止事件冒泡传播
5. 阻止事件默认行为
6. ajax兼容问题

前端常见浏览器兼容性问题解决方案_touch-action 浏览器兼容-CSDN博客

  • 0
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值