nuxt项目总结
技术栈选择
由于项目需要考虑SEO,而我又不想写原生HTML+CSS+JS,想使用Vue去写
所以打算使用只学过没用过的Nuxt(SSR)框架去写,同时在Javascript与Typescript语言的选择,
选择Typescript的原因是:没用过,想Try Try;如果选择了Typescript,那就不能直接用Vue的OptionAPI,选择vue-property-decorator的ClassAPI更加友好,而兼容nuxt的classAPI的依赖有nuxt-property-decorator
存储方式选择localforage代替localstorage
原因:1.localstorage存储比较慢,但localforage能够轻松实现异步离线存储。promise格式
2.localstorage仅支持字符串,存储前、获取后需要进行字符串序列化与反序列化,localforage可以直接存非字符串数据。
参考地址:https://www.cnblogs.com/lhb25/p/localforage-offline-storage-improved.html
// 以下nuxt.config.js配置除了TDK,其余请自行配置,不做详细描述
UI库看自己喜欢。
选择:Nuxt、typescript、Scss、axios、localforage、ElementUI
依赖
// 主要依赖
"nuxt": "^2.14.1", // 内置有很多依赖了 包括axios
"nuxt-property-decorator": "^2.7.2",
"nuxt-sass-resources-loader": "^2.0.5",
"vue-property-decorator": "^9.0.0",
"vuex-class": "^0.3.2"
"node-sass": "^4.14.1",
"sass-loader": "^9.0.2",
"@types/node": "^14.6.3",
nuxt的生命周期(及其重要,学习框架必须了解生命周期,及其原理)
基于nuxt的axios二次封装
// plugins/axios.ts
1.与往常vue做的封装样子不一样,思想也是一样,导出ts的interface也是一样的
import { AxiosRequestConfig, AxiosInstance, AxiosResponse } from 'axios'
export default function ({$axios, redirect, app, ssrContentxt, store}: any) {
const axios = $axios as AxiosInstance
}
2.请求头的设置 (服务端与客户端的请求头设置是不一样)
- 服务端
/middleware/stats
- 下列代码只在服务端设置请求头,但不能清除请求头
export default function ({ ssrContext, $axios, app }) {
if (ssrContext && ssrContext.req.headers.cookie && ssrContext.req.headers.cookie.indexOf('token=') != -1) {
let tmpArr = ssrContext.req.headers.cookie.split(';')
let tokenArr = ''
tokenArr = tmpArr.filter((value) => {
return value.indexOf('token=') != -1
})
let cookieArr = tokenArr[0].split('token=');
$axios.defaults.headers.token = cookieArr[1]
} else {
$axios.defaults.headers.token = ''
}
}
- 客户端
/plugins/axios
因为localforage是异步的,所以用两种方式去放入token(cookies,localforage)
let token: any = Cookies.get('token')
const data = value.data
if (process.client) {
if (!token) {
await localforage.getItem('userInfo').then((res: any) => {
if (res && res.token) {
token = res.token
}
})
}
value.headers.token = token
}
为何能够SEO优化
- SSR
使用asyncData、fetch、nuxtServerInit
三者都是nuxt在服务端的hooks
// 例子
// axios.api与apiResult都是封装好的
// return是一个对象,这个对象相当于data() {return {}} <---return出来的对象
// 只有page有asyncData、fetch,子components是不存在hooks
asyncData({$axios}) {
try {
var homeListData: any = {
page: 1,
page_size: 4
}
const homeFileList = await app.$axios.api('homeList', homeListData) as apiResult // 首页列表
return {
homeFileList: homeFileList.result
}
}catch(error) {
}
}
// nuxtServerInit是store的hooks
async nuxtServerInit({commit}, {$axios}) {
// 获取轮播图
var data = {}
await app.$axios.api('homeBanner').then((res: apiResult) => {
if (res.code === 200) {
data = res.result
}
}).catch((e: any) => {
console.log(e)
})
}
-
TDK(title、description、keywords)
-
全局配置
/nuxt.config.js
head: {
// title: process.env.npm_package_name || '',
title: 'xxx',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{ hid: 'description', name: 'description', content: 'xxxx', keywords: 'xxxx' }
]
}
CSR 和 SSR 最大的区别在于前者的页面渲染是 JS 负责进行的,而后者是服务器端直接返回 HTML 让浏览器直接渲染
使用SSR所解决的问题
1.提高SEO(搜索引擎优化)
2.提高首屏性能。
SSR渲染原理
1.简单来说就是,挂载服务端,让服务端去请求数据,然后去服务端去组装html,将这个html返回到客户端
2.总所周知,MVVM框架的模式都是基于虚拟DOM去渲染,然后通过一些方法转化为真是DOM,挂载到浏览器上去。虚拟DOM其实就是JS对象形成的一颗树。SSR所需要做的就是,MVVM打包出来的js库,去做同构处理。所谓同构就是,浏览器运行一遍,服务端又运行一遍。
3.SSR的核心就是同构
为什么要用引入node中间层
如果手写SSR,不到万不得已,其实不要用。它所解决最大的难点在与SEO,但同时也付出了昂贵的成本。不仅因为服务端渲染需要更加复杂的处理逻辑,还因为同构的过程需要服务端和客户端都执行一遍代码,这虽然对于客户端并没有什么大碍,但对于服务端却是巨大的压力,因为数量庞大的访问量,对于每一次访问都要另外在服务器端执行一遍代码进行计算和编译,大大地消耗了服务器端的性能,成本随之增加。如果访问量足够大的时候,以前不用 SSR 的时候一台服务器能够承受的压力现在或许要增加到 10 台才能抗住。痛点在于 SEO,但如果实际上对 SEO 要求并不高的时候,那使用 SSR 就大可不必了。
回归问题,为什么要引入 node 作为中间层呢?它是处在哪两者的中间?又是解决了什么场景下的问题?
在不用中间层的前后端分离开发模式下,前端一般直接请求后端的接口。但真实场景下,后端所给的数据格式并不是前端想要的,但处于性能原因或者其他的因素接口格式不能更改,这时候需要在前端做一些额外的数据处理操作。前端来操作数据本身无可厚非,但是当数据量变得庞大起来,那么在客户端就是产生巨大的性能损耗,甚至影响到用户体验。在这个时候,node 中间层的概念便应运而生。
它最终解决的前后端协作的问题。
一般的中间层工作流是这样的:前端每次发送请求都是去请求 node 层的接口,然后 node 对于相应的前端请求做转发,用 node 去请求真正的后端接口获取数据,获取后再由 node 层做对应的数据计算等处理操作,然后返回给前端。这就相当于让 node 层替前端接管了对数据的操作。
使用SSR最难处理的点
- store
- 原本MVVM框架的Hooks
- 同构
- 虚拟DOM
- 路由
- store
Vue(nuxt)+typescript配置
在根目录下创建文件vue-shim.d.ts声明文件(其实自己创建个typing文件夹更好)
declare module "*.vue" {
import Vue from 'vue'
export default Vue
}
IDE就不会报错了
同时还要考虑nuxt
在tsconfig.json中的配置
{
"compilerOptions": {
"target": "ES2018",
"module": "esnext",
"strict": false,
"jsx": "preserve",
"moduleResolution": "Node",
"experimentalDecorators": true,
"importHelpers": false,
"esModuleInterop": true,
"baseUrl": ".",
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"lib": [
"ESNext",
"esnext.asynciterable",
"dom",
"dom.iterable",
"scripthost"
],
"allowJs": true,
"sourceMap": true,
"noEmit": true,
"paths": {
"~/*": [
"./*"
],
"@/*": [
"./*"
],
"@/components": ["./components"],
"@/plugins": ["./plugins"]
},
"types": [
"@types/node",
"@nuxt/types"
]
},
"exclude": [
"node_modules",
".nuxt",
"dist"
]
}
前端安全问题
- xss跨站脚本攻击原理?如何进行?防御手段?
如何进行:如何XSS是指恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,进而添加一些代码,嵌入到web页面中去。使别的用户访问都会执行相应的嵌入代码。从而盗取用户资料、利用用户身份进行某种动作或者对访问者进行病毒侵害的一种攻击方式。
主要原理:过于信任客户端提交的数据!
防御手段:不信任任何客户端提交的数据,只要是客户端提交的数据就应该先进行相应的过滤处理然后方可进行下一步的操作。
- CSRF跨站请求伪造原理?如何进行?防御手段?
如何进行:当你在某网页登录之后,在没有关闭网页的情况下,收到别人的链接。例如:http://127.0.0.1/dvwa/vulnerabilities/csrf/?password_new=1&password_conf=1&Change=Change#
点击链接,会利用浏览器的cookie把密码改掉。
主要原理:在没有关闭相关网页的情况下,点击其他人发来的CSRF链接,利用客户端的cookie直接向服务器发送请求。
防御手段:检测Referer
Anti-CSRF token机制
业务上要求用户输入原始密码(简单粗暴),攻击者在不知道原始密码的情况下,无论如何都无法进行CSRF攻击。
- Sql脚本注入原理?如何进行?防御手段?
如何进行:利用现有应用程序,将(恶意)的SQL命令注入到后台数据库引擎执行的能力,它可以通过在Web表单中输入(恶意)SQL语句得到一个存在安全漏洞的网站上的数据库,而不是按照设计者意图去执行SQL语句。
主要原理:通过把SQL命令插入到Web表单提交或输入域名或页面请求的查询字符串,最终达到欺骗服务器执行恶意的SQL命令
防御手段:使用预编译,绑定变量(推荐)。检查数据类型。过滤特殊字符和语句。页面不错误回显。
- web上传漏洞原理?如何进行?防御手段?
如何进行:用户上传了一个可执行的脚本文件,并通过此脚本文件获得了执行服务器端命令的能力。
主要原理:当文件上传时没有对文件的格式和上传用户做验证,导致任意用户可以上传任意文件,那么这就是一个上传漏洞。
防御手段:
-
最有效的,将文件上传目录直接设置为不可执行,对于Linux而言,撤销其目录的’x’权限;实际中很多大型网站的上传应用都会放置在独立的存储上作为静态文件处理,一是方便使用缓存加速降低能耗,二是杜绝了脚本执行的可能性;
-
文件类型检查:强烈推荐白名单方式,结合MIME Type、后缀检查等方式;此外对于图片的处理可以使用压缩函数或resize函数,处理图片的同时破坏其包含的HTML代码;
-
使用随机数改写文件名和文件路径,使得用户不能轻易访问自己上传的文件;
-
单独设置文件服务器的域名