目录
2.1 在static目录下新建js文件 page:flexble
5.nuxt 使用VueAwesomeSwiper插件,展示轮播及缩略图
6.IntersectionObserver API 使用教程
前言:
开发完成的这个项目是基于nuxt.js框架和typescript语法的服务端渲染项目,具有良好的SEO体验。目前项目处于预发布阶段。开发用时约30天,测试约15天,共约26个页面,接口约50个,封装了一些公共组件,也用到一些三方组件,如seiper轮播图插件,加密插件、day.js插件。
兼容:项目兼容几乎所有浏览器,IE兼容到11
重点功能:SEO搜索引擎优化、直播功能、直播回顾功能、权限验证
1.总结(个人感受)
1.对于UI页面的精确度已经达到了惨绝人寰的程度,UI人员精确到1px就罢了,今天跑过来说调整一下这个地方,明天又跑来说这个地方再改下…(绝对是巨坑,最坑没有之一,浪费LZ大量的时间和精力,无力吐槽…)
2.nuxt.js框架的使用,是有坑的,比如说获取window对象,比如获取vue对象,还有个大坑就是,因为是服务端渲染。一般基地址就是两个,一个开发地址一个线上地址,但是在这里至少需要3个地址,一个开发的,一个客户端请求的,一个服务器请求的,要做判断区分。
3.项目中的首页大量使用seiper轮播图插件,高版本比如7兼容性不好,项目用的4,但是也存在问题,一个巨坑是缩略图轮播,thumbs老是报错(初始化正常,切换tab栏回去的时候报错,刷新页面就好),说找不到addclass,位置在init 7700多行,死活排查不出来,最后我是通过在错误页面强行使用this.$router.go(0)刷新页面。
4.关于三方插件,如时间格式转换的,推荐day.js插件,非常好用。再比如加密插件,使用的是crypto.js加密插件,使用也是非常简单,存的是时候加密一下,取出来的时候再解密一下
5.使用如elementUI组件库的时候,要修改它们样式,记得使用::v-deep
2.rem适配[1920*1080设计图]
2.1 在static目录下新建js文件 page:flexble
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
// var rem = docEl.clientWidth / 10
// docEl.style.fontSize = rem + 'px'
var width = docEl.getBoundingClientRect().width
// 当屏幕超过1920px以后就不在随着屏幕的变大而变大了
if (width / dpr > 1920) {
width = 1920 * dpr
}
// 当屏幕小于1300px以后就不再随着屏幕的变小而变小了
// if (width / dpr < 1300) {
// width = 1300 * dpr
// }
var rem = width / 19.2
docEl.style.fontSize = rem + 'px'
window.rem = rem
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
2.2 在assets文件夹下新建css文件
@use "sass:math";
$base-width: 1920;
$base-font_size: math.div(1920 , 19.2);
@function rem($px){
@return math.div($px , $base-font_size) * 1rem;
// @return $px * 1px
}
/** 字体 **/
$large-font_size: 16px ;
$medium-font_size: 14px;
$small-font_size: 12px;
$title-size: rem(44);
/** 颜色 **/
$primary-color: #C90000;
$bottom-color: #D10000;
$oss_url:'//oss-guanwang.yuceyingjia.com/';
$base-oss_url: '//oss-guanwang.yuceyingjia.com/newGWWeb/';
:export {
ossUrl: $base-oss_url;
}
2.3 在nuxt.config.js设置配置
// 引入全局scss 变量
styleResources: {
// your settings here
scss: ['./assets/css/variables.scss'],
hoistUseStatements: true // Hoists the "@use" imports. Applies only to "sass", "scss" and "less". Default: false.
},
script: [
{ src: '/js/page-flexible.js'}
]
2.4 页面中使用
font-size:rem(15)
3. 登录注册页
登录、注册、忘记密码(重置)均在一个页面上,有密码校验、本地持久化,记住密码、短信验证码、加密、路由跳转等功能
封装成一个组件(使用cookie):
<template>
<div>
<div class="container1">
{
{$route.query.zc}}
<!-- 左侧注册区域 -->
<div class="left">
<div class="zhuce">
<!-- logo title -->
<div class="logo"><img src="~/assets/img/login/LOGO.png" alt="" /></div>
<!-- 注册表单区域 -->
<div class="formTable">
<el-form :rules="rules" ref="formData" :model="formData">
<el-form-item prop="mobile" v-show="wangji || !forGet">
<el-input
v-model="formData.mobile"
placeholder="请输入您的手机号"
>
</el-input>
</el-form-item>
<el-form-item
prop="email"
class="article"
v-if="cLogin || forGet "
v-show="wangji|| !forGet"
>
<el-input
v-model="formData.email"
maxlength="40"
placeholder="手机验证码"
class="input"
>
</el-input>
<span class="span" @click="getCMS" v-if="cLogin ">
<span v-show="isC == false">获取验证码</span>
<span v-show="isC == true">{
{
isActive ? "重新获取" : count + "s"
}}</span></span
>
</el-form-item>
<!-- <el-form-item prop="Yzm" v-if="cLogin || forGet" v-show="wangji|| !forGet"> -->
<el-form-item prop="Yzm" v-if="imgUrl">
<el-input
v-model="formData.Yzm"
maxlength="40"
placeholder="图形验证码"
class="input"
>
</el-input>
<img :src="imgUrl" alt="" class="img"/>
</el-form-item>
<el-form-item prop="miMa" v-if="!forGet">
<el-input
v-model="formData.miMa"
placeholder="您的账号密码"
></el-input>
</el-form-item>
<!-- 修改密码区域 forGet控制 -->
<el-form-item prop="newPassword" v-if="!wangji && forGet">
<el-input type="password" placeholder="请输入新密码" v-model="formData.newPassword"></el-input>
</el-form-item>
<el-form-item prop="repPassword" v-if="!wangji&& forGet">
<el-input type="password" placeholder="请输入确认密码" v-model="formData.repPassword"></el-input>
</el-form-item>
<!-- 记住密码、忘记密码区域 -->
<div class="middle" v-if="!cLogin">
<div class="left">
<el-checkbox v-model="checked">记住密码</el-checkbox>
</div>
<div class="right" v-if="!forGet"><span @click="missCode">忘记密码</span></div>
</div>
<!-- 提交表单按钮 -->
<el-form-item align="center" class="div" v-if="!wangji">
<el-button
size="mini"
type="primary"
:loading="loading"
@click="submitForm('formData')"
>{
{buttonText}}</el-button
>
</el-form-item>
<el-form-item align="center" class="div" v-else>
<el-button
size="mini"
type="primary"
:loading="loading"
@click="next"
>下一步</el-button
>
</el-form-item>
<!-- 底部 还没有账号? 立即注册区域 -->
<div class="bottom" v-if="!wangji" >
<span class="left">{
{cLogin ? '已有账号?':'还没有账号?'}}</span>
<span class="right" @click="clickLogin">{
{cLogin ? '立即登录':'立即注册'}}</span>
</div>
</el-form>
</div>
</div>
</div>
<!-- 右侧图片区域 -->
<div class="right">
<img src="~/assets/img/login/bg.png" alt="" />
<div class="img"><img src="~/assets/img/login/login_slogin.png" alt=""></div>
<div class="zhezhao"></div>
<!-- <div class="textIMG">
<h3>让投资更简单更理性</h3>
<p>Make investment simpler and</p>
<p>more rational</p>
</div> -->
</div>
</div>
</div>
</template>
<script>
import CryptoJS from "crypto-js";//加密
import { isvalidUsername, isvalidMobile, isvalidEmail, code, password } from '@/utils/validate'
import axios from 'axios'
// import { Alert } from 'element-ui'
const TIME_COUNT = 60
export default {
props: {
loading: { // 是否点击确定按钮
type: Boolean,
default: false
}
},
methods: {
//提交表单
submitForm (formName) {
this.$refs[formName].validate((valid) => {
if (valid) {
// 校验通过,提交数据
this.getIMGCode()//调图形验证码,默认为空
if(this.forGet){
// 重置密码
this.getReset()
}else{
if(this.cLogin===false){
// 做登录
this.getLogin ()
}else{
// 做注册
this.getZhuCe ()
}
}
} else {
// 验证不通过
return false
}
})
},
// 封装的倒计时效果
getCMS () {
this.isC = true
if (!this.timer) {
this.count = TIME_COUNT
this.isActive = false
this.getCMSCode() //获取验证码(ip)----60秒内不能重复发送--
this.timer = setInterval(() => {
if (this.count > 0 && this.count <= TIME_COUNT) {
this.count--
} else {
this.isActive = true
clearInterval(this.timer)
this.timer = null
}
}, 1000)
}
},
// 做登录
async getLogin () {
let data1 = await axios.post("https://app-gw-test.365ycyj.com/UserApi/50000/UserLogin", {
username: this.formData.mobile,
password: this.formData.miMa,
})
console.log(data1, 'login--------------11111111111111111')
if(data1.data.Msg == 'success'){
this.$message({
message: '恭喜登录成功!',
type: 'success'
});
console.log(data1.data,'wwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwwww');
// 如果记住密码的话
if(this.checked){
localStorage.setItem("rememberPsw",true)
let cipherText = CryptoJS.AES.encrypt(this.formData.miMa, "secretkey123").toString();
this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token,userInfo:this.formData.mobile,pwd:this.formData.miMa})
this.$cookies.set("accessToken",data1.data.Data.Token,{maxAge:60*60*24*3}) //保存3天
this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*24*3}) //保存3天
this.$cookies.set("pwd",cipherText,{maxAge:60*60*24*3}) //保存3天
}else{
// 不记住密码,不保存密码
localStorage.setItem("rememberPsw",false)
let cipherText = CryptoJS.AES.encrypt(this.formData.miMa, "secretkey123").toString();
this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token,userInfo:this.formData.mobile})
// this.$cookies.set("pwd",'') //
// this.$cookies.set("accessToken",'') //
this.$cookies.set("accessToken",data1.data.Data.Token,{maxAge:60*60*4}) //保存4小时
this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*4}) //保存4小时
this.$cookies.set("pwd",cipherText,{maxAge:60*60*4}) //保存4小时
// this.$cookies.set("accessToken",data1.data.Data.Token,{maxAge:60*60*4}) //保存4小时
// this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*4}) //保存4小时
}
this.$router.push(this.$route.query.return_url || '/') //登录后跳转到原位置或者首页
// this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token2,userInfo:this.formData.mobile})
// this.$cookies.set("accessToken",data1.data.Data.Token2,{maxAge:60*60*24*3}) //保存3天
// this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*24*3}) //保存3天
// this.$router.go(0); //刷新页面
}else {
this.$message.error(data1.data.Msg);
// this.$router.push({path: '/', query:{id: 3}});
}
},
// 做注册
async getZhuCe () {
let data1 = await axios.post("https://app-gw-test.365ycyj.com/UserApi/50000/MReg", {
tel: this.formData.mobile,
pwd: this.formData.miMa,
smscode:this.formData.email,
app: '官网',
issendmsg: '1',
qudaotype: '2',
clienttype: '0',
mac: '',
clientagent: 'pc_ycyj',
qudaoma: '',
})
console.log(data1, 'login--------------11111111111111111')
if(data1.data.Msg == 'success'){
this.$message({
message: '恭喜您注册成功!请登录',
type: 'success'
});
this.cLogin = false //去登陆
// 如果是注册的话,那么登录后应该下载软件
this.isDownPC = true
// this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token2,userInfo:this.formData.mobile})
// this.$cookies.set("accessToken",data1.data.Data.Token2,{maxAge:60*60*24*3}) //保存3天
// this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*24*3}) //保存3天
// this.formData.mobile = '' // 关闭弹框
// this.$router.go(0); //刷新页面
}else {
this.$message.error(data1.data.Msg);
}
},
// 接口--获取短信验证码--ip限制--默认为空GetSMSCodeByWeb
async getCMSCode(){
let data1 = await axios.post("https://app-gw-test.365ycyj.com/UserApi/50000/GetSMSCodeByWeb", {
tel: this.formData.mobile || '15805494663',
imgcode:this.formData.Yzm ||'',
yingjiatype:'0',
type:'0'
})
console.log(data1,'ceshi 短信验证码……………………………………………………');
},
// 接口--获取图形验证码--ip限制
async getIMGCode(){
let data1 = await axios.post("https://app-gw-test.365ycyj.com/UserApi/50000/GetImgCodeByWeb")
if(data1.Data){
this.imgUrl = data1.Data.ImgSrc
}
},
// 做忘记密码--密码重置
async getReset () {
let data1 = await axios.post("https://app-gw-test.365ycyj.com/UserApi/50000/ResetPassword", {
tel: this.formData.mobile,
password: this.formData.newPassword,
smscode:this.formData.email,
clienttype: '0',
})
console.log(data1, 'getReset--------------11111111111111111')
if(data1.data.Msg == 'success'){
this.$message({
message: '恭喜您重置成功!',
type: 'success'
});
// 如果记住密码的话
if(this.checked){
this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token,userInfo:this.formData.mobile})
this.$cookies.set("accessToken",data1.data.Data.Token,{maxAge:60*60*24*3}) //保存3天
this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*24*3}) //保存3天
}else{
this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token,userInfo:this.formData.mobile})
this.$cookies.set("accessToken",data1.data.Data.Token,{maxAge:60*60*4}) //保存4小时
this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*4}) //保存4小时
}
this.$router.push(this.$route.query.return_url || '/') //登录后跳转到原位置或者首页
// this.cLogin = false //去登陆
// this.$store.commit('UPDATE_ALL_STATE',{accessToken:data1.data.Data.Token2,userInfo:this.formData.mobile})
// this.$cookies.set("accessToken",data1.data.Data.Token2,{maxAge:60*60*24*3}) //保存3天
// this.$cookies.set("userInfo",this.formData.mobile,{maxAge:60*60*24*3}) //保存3天
// this.formData.mobile = '' // 关闭弹框
// this.$router.go(0); //刷新页面
}else {
this.$message.error(data1.data.Msg);
}
},
// missCode+clickLogin做忘记密码、注册、登录切换效果
missCode(){
this.wangji = true
this.forGet=!this.forGet
this.cLogin = true
},
clickLogin(){
this.cLogin=!this.cLogin
this.forGet = false
},
// 点击下一步
next(){
this.wangji = false
}
},
data () {
var checkNickName = (rule, value, callback) => {
if (!value) {
callback(new Error('姓名不能为空'))
} else if (value.length > 30) {
callback(new Error('最多30个字符'))
} else {
callback()
}
}
var checkMobile = (rule, value, callback) => {
if (!value) {
callback(new Error('手机号不能为空'))
} else if (!isvalidMobile(value)) {
callback(new Error('手机号不合法'))
} else {
callback()
}
}
var checkCode = (rule, value, callback) => {
if (!value) {
callback(new Error('验证码不能为空'))
} else if (!code(value)) {
callback(new Error('验证码不合法'))
} else {
callback()
}
}
var checkPass = (rule, value, callback) => {
if (value === '') {
callback(new Error('密码不能为空'))
}
else if (!password(value)) {
callback(new Error('密码不合法'))
}
else {
callback()
}
}
var checkYZM = (rule, value, callback) => {
if (!value) {
callback(new Error('图形验证码不能为空'))
} else if (!code(value)) {
callback(new Error('图形验证码不合法'))
} else {
callback()
}
}
// 校验新密码
const validatePassword = (rule, value, callback) => {
// console.log('value', value)
if(value.length < 6) {
callback(new Error('新密码不能少于6位'))
}else {
callback()
}
};
// 校验确认密码是否一致
const validateRepPassword = (rule, value, callback) => {
if(value !== this.formData.newPassword) {
callback(new Error('两次输入的密码不一致'))
}else {
callback()
}
};
return {
rules: {
mobile: [{ required: true, validator: checkMobile, trigger: 'blur' }],
email: [{ required: true, validator: checkCode, trigger: 'blur' }],
miMa: [{ required: true, validator: checkPass, trigger: 'blur' }],
Yzm: [{ required: true, validator: checkYZM, trigger: 'blur' }],
newPassword: [
{ required: true, message: '新密码不能为空', trigger: 'blur' },
{ validator: validatePassword, trigger: 'blur' }
],
repPassword: [
{ required: true, message: '确认密码不能为空', trigger: 'blur' },
{ validator: validateRepPassword, trigger: ['change', 'blur'] }
]
},
ip: '1.1.1.1',
area: '北京市',
brower: 'chrome',
os: 'windows7',
// mobile:this.formData.mobile,
imgUrl: "",
formData: {
mobile:this.$cookies.get('userInfo')? CryptoJS.AES.decrypt(this.$cookies.get('userInfo'), "secretkey123").toString(CryptoJS.enc.Utf8):'',
email: '',
miMa:this.$cookies.get('pwd')? CryptoJS.AES.decrypt(this.$cookies.get('pwd'), "secretkey123").toString(CryptoJS.enc.Utf8):'',//解密
code: "",
Yzm: '',
newPassword:'',
repPassword:''
},
isActive: false,
count: 0,
timer: null,
isC: false,
cLogin:false,//切换登录和注册
checked:false,//是否记住密码
forGet:false,//是否点击忘记密码
wangji:false,//忘记密码密码
isDownPC:false,//是不是下载pc软件
borHeight:1000,//浏览器可视区域的高度
}
},
async created() {
// 如果是点击注册,那么页面显示注册
if(this.$route.query.zc == 1){
this.cLogin = true
}
// 这里测试同一ip超过5次哪个 ,有数据就会显示图形验证码
// const data1 = await this.$api.UserApi.GetImageVeriFicationCode();
// this.imgUrl = data1.ImgSrc
// this.getIMGCode() //这个是获取图形验证码、ip限制的
},
mounted () {
if(localStorage.getItem("rememberPsw") == 'true'){
this.checked = true
}else if(localStorage.getItem("rememberPsw") == 'false'){
this.checked = false
}
},
// 登录按钮的文字显示
computed: {
buttonText() {
if(this.forGet === true){
return "确定"
}else{
if(this.cLogin){
return "快速注册领取3天VIP"
}else if(!this.cLogin){
return "立即登录"
}
}
}
}
}
</script>
<style lang="scss" scoped>
.span {
position: absolute;
top: 16.2%;
right: 8%;
color: #c90000;
font-family: PingFang SC;
font-size: rem(18);
}
// 修改提交按钮的样式
.el-button{
outline: 0 none;
border: 0 none;
width: rem(600);
height: rem(60);
background: #C90000;
margin-top: rem(50);
font-size: rem(30);
font-family: PingFang SC;
font-weight: 600;
color: #FFFFFF;
}
// 修改输入框的颜色
::v-deep .el-input__inner{
background-color: #f6f6f6;
}
// 修改输入框的边框样式
::v-deep .el-input__inner{
border: 1px solid rgba(255,255,255,0);
border-bottom: 2px solid #f6f6f6;
font-size: rem(20);
color: #1E1E1E;
margin: rem(20) 0;
border-radius: 0;
}
// 修改校验错误时的样式
::v-deep .el-form-item.is-error .el-input__inner, .el-form-item.is-error .el-input__inner:focus, .el-form-item.is-error .el-textarea__inner, .el-form-item.is-error .el-textarea__inner:focus, .el-message-box__input input.invalid, .el-message-box__input input.invalid:focus {
border-color: #f6f6f6;
border-bottom: 2px solid #C90000;
}
// 修改校验错误时弹出字体
::v-deep .el-form-item__error {
color: #C90000;
font-size: rem(16);
}
// 修改选中记住密码样式
::v-deep .el-checkbox__input.is-checked .el-checkbox__inner, .el-checkbox__input.is-indeterminate .el-checkbox__inner {
background-color: #C90000;
border-color: #C90000;
}
::v-deep .el-checkbox__input.is-checked+.el-checkbox__label {
width: rem(94);
height: rem(23);
font-size: rem(16);
font-family: PingFang SC;
font-weight: 400;
color: #1E1E1E;
}
::v-deep .el-checkbox{
width: rem(94);
height: rem(23);
font-size: rem(16);
font-family: PingFang SC;
font-weight: 400;
color: #1E1E1E;
}
::v-deep .el-checkbox__label {
font-size: rem(16);
}
.container1 {
height:100%;
width:100%;
display: flex;
box-sizing: border-box;
.left {
flex: 3;
overflow: hidden;
width: 70%;
background-color: #f6f6f6;
.logo {
width: rem(296);
height: rem(70);
margin: rem(90) rem(422) rem(60);
}
.formTable{
width: rem(600);
height: 100%;
margin: 0 rem(270);
.middle{
display: flex;
justify-content: space-between;
.right{
text-align: right;
width: 93px;
height: rem(23);
font-size: rem(16);
font-family: PingFang SC;
font-weight: 400;
color: #C90000;
}
.left{
width: rem(94);
height: rem(23);
font-size: rem(16);
font-family: PingFang SC;
font-weight: 400;
color: #1E1E1E;
}
}
.bottom{
width: rem(238);
height: rem(23);
margin:rem(30) auto;
line-height: rem(23);
text-align: center;
.left{
color: #1F1F1F;
font-size: rem(14);
}
.right:hover{
cursor: pointer;
}
.right{
color: #C90000;
font-size: rem(16);
font-family: PingFang SC;
font-weight: 700;
}
}
}
}
.right {
position: relative;
width: 100%;
flex: 2;
overflow: hidden;
height: 100%;
.textIMG{
position: absolute;
top: 20%;
left: 0;
h3{
margin-left: rem(30);
font-size: rem(26);
font-family: PingFang SC;
font-weight: 600;
color: #C90000;
}
p{
margin-left: rem(30);
margin-top: rem(10);
font-size: rem(20);
font-family: D-DIN;
font-weight: bold;
color: #3B3B3B;
}
}
.img{
position: absolute;
top: rem(200);
left: rem(60);
width: rem(448);
height: rem(138);
z-index: 2;
img{
width: 100%;
height: 100%;
}
}
.zhezhao{
position: absolute;
height: 100%;
top: 0;
left: 0;
width: rem(140);
background-color: #f2f2f2;
opacity: 0.7;
z-index: 1;
}
img {
width: 100%;
height: 100%;
}
}
.img {
overflow: hidden;
width: rem(200);
height: rem(50);
position: absolute;
bottom: 23%;
right: 0%;
}
}
</style>
4.seo优化
需求:
实现:
其实就是设置nuxt.config.js里面的config中设置全局head对象,也可在每个页面的head中设置
在页面里面:
<template>
<h1>{
{ title }}</h1>
</template>
<script>
export default {
data() {
return {
title: 'Home page'
}
},
head() {
return {
title: this.title,
meta: [
{
hid: 'description',
name: 'description',
content: 'Home page description'
}
]
}
}
}
</script>
nuxt官网地址:
Nuxt - Meta Tags and SEOhttps://nuxtjs.org/docs/features/meta-tags-seo
5.nuxt 使用VueAwesomeSwiper插件,展示轮播及缩略图
6.IntersectionObserver API 使用教程
7.IE11的CSS兼容性问题
8.pc端网页屏幕自适应适配方案(rem)
9.后台返回图形验证码 前端如何处理
10.nuxt中文官网
11.好用的日期处理插件day.js
下载安装全局安装后
在页面中就可随意使用
{
{$dayjs(item.StartTime).format("YYYY/MM/DD")}}