经验一:
在组件标签上绑定的所有事件(包括原生事件的名字click, input等等)
都是自定义事件,都需要组件内$emit来触发才行
万一组件内不支持这个原生事件名字
解决:@事件名.native="methods里方法名"
.native给组件内根标签,绑定这个原生的事件
经验二:
退出登录,重新登录,只走相关组件代码(异步dom切换,不会导致所有代码重新执行,App.vue不走)
效果不对:你换个账号它得重新请求用户数据呀
解决:
1.可以在登录页面,登录成功后,再发请求去拿到用户信息
2.可以在全局前置路由守卫中,写(路由跳转的时候,判断+获取)
经验三:
选择的图片文件,要给到img标签上做纯前端的预览
img标签的src值
*只能是图片的"链接地址"(外链http://开头,图片文件相对路径)
*或者是图片的base64字符串(而且字符串还必须是data:image/png;base64,图片转base64字符串)
解决
方式1: 使用**FileReader**来读取选择的前端文件, 转换为base64字符串, 给img标签渲染(此字符串是可以发给后台的)
// 1. 创建 FileReader 对象
const fr = new FileReader()
// 2. 调用 readAsDataURL 函数,读取文件内容
fr.readAsDataURL(e.target.files[0])
// 3. 监听 fr 的 onload 事件
fr.onload = e => {
// 4. 通过 e.target.result 获取到读取的结果,值是字符串(base64 格式的字符串)
目标对象 = e.target.result
}
方式2: 使用`URL.createObjectURL`方法, 也可以把File类型文件, 转成一个Blob类型的纯前端本地的链接(这个地址只能在js里内存里不能发给后台)
目标对象 = URL.createObjectURL(e.target.files[0])
经验四:
1.路由全局前置守卫判断当前vuex里是否有token
有token值证明刚才登录过, 无token值证明未登录
router.beforeEach((to, from, next) => {
const token = store.state.token
if (token) {
// 如果有token, 证明已登录
if (!store.state.userInfo.username) {
// 有token但是没有用户信息, 才去请求用户信息保存到vuex里
// 调用actions里方法请求数据
store.dispatch('initUserInfo')
// 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
}
next() // 路由放行
} else {
next('/login')
}
})
2.在主页删除本地的vuex数据, 刷新页面让vuex取出来空的token, 但是发现递归了
3.原因: 因为强制跳转到登录页也会让路由守卫再次触发, 又判断无token, 再次跳转登录页, 就递归了
4.解决: 登录页面应该是无需判断token的, 还有注册页面, 所以设置白名单, 无token要去这2个页面直接放行
// 无需验证token的页面
const whiteList = ['/login', '/reg'];
router.beforeEach((to, from, next) => {
const token = store.state.token
if (token) {
// 如果有token, 证明已登录
if (!store.state.userInfo.username) {
// 有token但是没有用户信息, 才去请求用户信息保存到vuex里
// 调用actions里方法请求数据
store.dispatch('initUserInfo')
// 下次切换页面vuex里有用户信息数据就不会重复请求用户信息
}
next() // 路由放行
} else {
// 如果无token
// 如果去的是白名单页面, 则放行
if (whiteList.includes(to.path)) {
next()
} else {
// 如果其他页面请强制拦截并跳转到登录页面
next('/login')
}
}
})
经验五:
如果用同一个按钮,想要做状态区分
1. 定义一个标记变量isEdit(true是编辑,flase是新增),还要定义本次要编辑的数据唯一id值,editId
2. 在点击修改的时候,isEdit改为true,editId保存要修改的数据id
3. 在点击新增按钮的时候,isEdt改为false, editId设置为空
4. 在点击保存按钮时(确定按钮时),就可以用isEdit变量做区分了
经验六:
小bug: (el-form和el-dialog和数据回显同时用,有bug)
复现:第一次打开网页,先点击修改,再点击新增,发现输入框竟然有值
原因:点击修改后,关闭对话框的时候,置空失效了
具体分析:主人公resetFields有问题
线索:Dialog的内容是懒渲染的,即在第一次被打开之前,传入的默认 slot不会被渲染到DOM上,第二次后续只是做css隐藏和显示
线索:vue数据改变(先执行同步所有)再去更新DOM(异步代码)
无问题:第一次打开网页,先点击新增按钮-〉 dialog出现-〉el-form组件第一次挂载(关联的addForm对象的属性的值为空字符串) el-form组件内绑定了初始值,所以后续调用resetFields的时候,它可以用到空字符串初始值来重置
有问题:第一次打开网页,先点击修改按钮 -〉虽然dialog变量为true了但是同步代码把addForm对象里赋值了(默认值)->DOM更新异步-> dialog出现 -〉 el-form组件第一次挂载(使用addFonrm内置做回显然后第一次el-form内绑定了初始值(有值))->以后做重置,它就用绑定的带值的做重置
解决:
让el-dialog第一次挂载el-form时,先用addForm空字符串初始值绑定到内部,后续用作resetFields重置
所以让真实DOM先创建并在内部绑定好"复制"好初始值
接着我们真实DOM更新后绑定好了,咱们再给数据回显
注意:我们给v-model对象赋值只是影响当前显示的值,不会影响到resetFields复制的初始值
经验七:
$confirm内部虽然是一个确认提示框,但是它借用了Promise语法来管理,点击确定它的状态为兑现成功状态返回' confirm',如果用户点击了取消按钮,此Promise对象状态为拒绝状态,返回' cancel'字符串
//知识点回顾:
1. await只能用在被async修饰的函数内
async修饰:就是在当前函数名左边加async关键字,如果没有函数名,在形参的左边加async
原因:async修饰的函数就是异步函数,如果此函数被调用,总是返回一个全新Promise对象
而且本次函数调用因为是异步函数,所以外面的同步代码继续执行,而await暂停代码只能暂停async函数内的,等待await后面异步结果
2. await只能拿到成功的结果并放行往下走,如果失败内部会向浏览器控制台抛出错误并不会让await往下走代码
经验八:
富文本
基于 vue-quill-editor 实现富文本编辑器:https://www.npmjs.com/package/vue-quill-editor
1. 运行如下的命令,在项目中安装富文本编辑器:
cnpm i vue-quill-editor
2. 在项目入口文件 `main.js` 中导入并全局注册富文本编辑器
// 导入富文本编辑器
import VueQuillEditor from 'vue-quill-editor'
// 导入富文本编辑器的样式
import 'quill/dist/quill.core.css'
import 'quill/dist/quill.snow.css'
import 'quill/dist/quill.bubble.css'
// 全局注册富文本编辑器
Vue.use(VueQuillEditor)
使用:
<quill-editor v-model="pubForm.content"></quill-editor>
经验九:
scoped属性作用:让style里的选择器,只能选中当前组件的标签(为了保证样式的独立性,不影响别的组件)
scoped原理:(多加了一个data-v的属性选择器)
webpack打包的时候,会给组件标签上添加相同data-v-hash值,然后也会给所有选择器后面加上一个[data-v-hash]值的属性选择器
<标签data-v-390246 class="my_a"></标签>//选择器会变成.my_a[data-v-390246]
重要注意事项: scoped只会给当前组件所有原生标签添加data-v-hash值属性,还会给组件标签内根标签添加data-v-hash值属性,组件内的标签不会添加
解决:Vue提供了一个::v-deep样式语法,设置后,可以把属性选择器被自动添加到左侧
总结: scoped不会给组件内的标签添加data-v属性,所以你要用: :v-deep 穿透选择组件内的标签设置样式
经验十:
1. 在 `artList.vue` 组件的模板结构中,添加文章封面对应的 DOM 结构:
<el-form-item label="文章封面">
<!-- 用来显示封面的图片 -->
<img src="../../assets/images/cover.jpg" alt="" class="cover-img" ref="imgRef" />
<br />
<!-- 文件选择框,默认被隐藏 -->
<input type="file" style="display: none;" accept="image/*" ref="iptFileRef" />
<!-- 选择封面的按钮 -->
<el-button type="text">+ 选择封面</el-button>
</el-form-item>
2. 美化封面图片的样式:
// 设置图片封面的宽高
.cover-img {
width: 400px;
height: 280px;
object-fit: cover;
}
3. 为**选择封面**的按钮绑定点击事件处理函数
<!-- 选择封面的按钮 -->
<el-button type="text" @click="chooseImgFn">+ 选择封面</el-button>
4. 模拟**文件选择框**的点击事件
chooseImgFn() {
this.$refs.iptFileRef.click()
}
5. 监听**文件选择框**的 **change** 事件
<!-- 文件选择框,默认被隐藏 -->
<input type="file" style="display: none;" ref="iptFile" accept="image/*" @change="onCoverChangeFn" />
6. 定义 **onCoverChange** 处理函数如下
// 封面选择改变的事件
onCoverChangeFn (e) {
// 获取用户选择的文件列表
const files = e.target.files
if (files.length === 0) {
// 用户没有选择封面
this.pubForm.cover_img = null
} else {
// 用户选择了封面
this.pubForm.cover_img = files[0]
}
}
7. 在 data 中的 `pubForm` 对象上,声明 `cover_img` 属性,用来存储用户选择的封面
data() {
return {
// 表单的数据对象
pubForm: {
title: '',
cate_id: '',
content: '',
+ cover_img: null // 用户选择的封面图片(null 表示没有选择任何封面)
},
}
}
9. 在 `artList.vue` 组件中,导入默认的封面图片
// 导入默认的封面图片
import defaultImg from '@/assets/images/cover.jpg'
10. 在文件选择框的 `change` 事件处理函数中,根据用户是否选择了封面,动态设置封面图片的 src 地址:
// 监听文件选择框的 change 事件
onCoverChange(e) {
// 获取到用户选择的封面
const files = e.target.files
if (files.length === 0) {
// 用户没有选择封面
this.pubForm.cover_img = null
+ this.$refs.imgRef.setAttribute('src', defaultImg)
} else {
// 用户选择了封面
this.pubForm.cover_img = files[0]
+ const url = URL.createObjectURL(files[0])
+ this.$refs.imgRef.setAttribute('src', url)
}
}
经验十一:
标签和样式中,引入图片文件直接写"静态路径"(把路径放在js的vue变量里再赋予是不行的)
原因: webpack分析标签的时候,如果src的值是一个相对路径,它会去帮我们找到那个路径的文件并一起打包
打包时候,会分析文件的大小,小图转成base64字符串再赋予给src,如果是大图拷贝图片换个路径给img的src显示(运行时)
Vue变量中路径,赋予给标签,都会当做普通的字符串使用
以前:我们写的路径是在vscode看着文件夹写的(以前好使的原因:你用live Server/磁盘双击打开,它都能通过你的相对路径,在指定路径文件夹下,找到图片文件真身)
现在:写的模板代码,是要被webpack翻译处理转换的,你vscode里的代码,转换后打包到内存中/dist下,相对路径就会变化,运行时,你写的固定路径字符串就找不到那个文件真身
解决: js里引入图片,就用import引入,让webpack把它当做模块数据,是转换成打包后的图片路径还是base64字符串
注意:只有相对路径本地图片需要注意,如果你是一个http://外链的图片地址,就可以直接随便用
经验十二:
为何这个输入内容了,校验还不自己去掉?
原因:
content对应quill - editor富文本编辑器,它不是el提供表单标签
el-input等输入框的在blur事件时进行校验
下拉菜单,单选框,复选框,是在change事件时进行校验
quill-editor2个事件都没有,所以你输入内容也不会自动走校验
解决:
自己来给quill-editor绑定change事件(在文档里查到的它支持change事件内容改变事件)
规则校验对象里面的 trigger 要改成 change
content: [{ required: true, message: '请输入文章内容', trigger: 'change' }],
还要给目标标签 绑定change事件
// 富文本编辑器内容改变触发此事件方法
contentChangeFn() {
// 让表单单独校验富文本的规则
this.$refs.pubFormRef.validateField('content');
}
经验十三:
准备一个表单数据对象的容器FormData类是HTML5新出的专门为了装文件内容和其他的参数的一个容器
const fd = new FormData();
fd.append('参数名','值');
经验十四:
把表格里的时间格式化成YYYY-MM-DD HH:mm:ss格式
1. 安装格式化时间的第三方包 `dayjs`
yarn add dayjs
2. 在项目入口文件 main.js 中导入并使用 dayjs,定义全局属性, 对应函数
// 导入dayjs方法
import dayjs from 'dayjs'
// 定义时间格式化函数
Vue.prototype.$formatDate = (dateObj) => {
return dayjs(dateObj).format('YYYY-MM-DD HH:mm:ss')
}
3. 在 `artList.vue` 组件中,调用全局属性的方法, 对时间进行格式化:
<el-table-column label="发表时间" prop="pub_date">
<template v-slot="{ row }">
<span>{{ $formatDate(row.pub_date) }}</span>
</template>
</el-table-column>
经验十五:文章来源:https://www.toymoban.com/news/detail-602249.html
问题:先点击最后一个页码,切换每页显示条数2->3,总数不够,分页只能分到2
每页条数改变了,页码从3到2页改变了,2个事件都会触发
偶发性的bug:有的时候自动回到第二页有数据有的时候没有
知识点:2个网络请求一起发,谁先回来不一定
原因:所以可能第2页3条数据回来有值铺设,紧接着第3页的3条数据回来了,空数组所以页面就是空的
解决:当切换每页显示的条数,我们就把当前页码设置为1,而且标签里要设置
handleSizeChange(sizes) {
// sizes :当前需要每页显示的条数
// 因为Pagination的标签上已经加了.sync,子组件内会双向绑定到右侧vue变量上(q对象里pagesize已经改变了)
this.q.pagesize = sizes;
this.q.pagenum = 1;
// 文章的列表数据
this.initArtListFn();
},
经验十六:文章来源地址https://www.toymoban.com/news/detail-602249.html
由于element组件里面的不能使用事件,我们可以给事件后面添加一个.native的原生方法
即: @click.native="btnFn"
但是如果还是不行就使用template插槽的形式
<el-table-column label="文章标题">
<template slot-scope="scope">
<el-link type="primary" @click="showDetailFn(scope.row.id)">{{ scope.row.title }}</el-link>
</template>
</el-table-column>
经验十七:
后端返回图片链接地址的经验://为何后端返回的图片地址是半截的?
原因:因为服务器的域名可能会来回变化,所以数据库里的地址只有相对路径
要求:前端请求此图片的时候,后端告诉你图片文件真身所在的服务器域名,前端自己拼接前缀
经验十八:
积累知识
组件创建时,会用data里的默认值,让template里标签先渲染一次I
你的网络请求数据回来,data里变量发生了变化,会让template里使用此变量的地方再次更新dom
小问题:第一次渲染的时候无值可能会导致一些报错,但是效果还是出来了
解决:v-if先不让template里会报错的那个代码先屏蔽执行
经验十九:
问题:在最后一页,删除最后一条时,虽然页码能回到上一页,但是数据没有出现
原因:发现network里参数q.pagenum的值不会自己回到上一页,因为你的代码里没有修改过这个q.pagenum值,只是调用getArticleFn方法,带着之前的参数请求去了所以没数据
解决:在调用接口以后,刷新数组列表之前,对页码最一下处理
经验二十:
打包相关问题:
问题:打开index.html,出现了404问题
原因:
1.默认打包,index.html引入其他打包的文件使用的是绝对地址
地址是以/开头(要找到当前index.html打开时所在服务器的根地址(文件夹))
就得确保你的vscode+liserver插件打开时,vscode根目录得直接是dist文件夹
你如果把dist文件夹交给后台运维工程师部署到服务器上, dist下内容就得在服务器根目录才行
但是服务器一般会有多个项目,不让你用根目录直接放一个项目,很乱
解决: webpack让你写一个配置项publicPath(控制index.html引入其他资源前缀地址)
vue.config.js (脚手架配置文件,webpack配置文件)
publicPath:默认值'/'确保开发环境下,是这个值,因为开发服务器会把所有打包在内存里而且作为服务器的根目录文件夹,绝对地址
生产环境准备上线了,这个时候,就得用相对地址,publicPath:'./'
路径不以/开头其实就是./开头(默认的)
./可以让开发环境和生产环境都可以正常使用
为了严谨一些
开发环境:'/'
生产环境:'./'
问题:有无代码可以让他自己识别当前运行环境?
node里有个内置的环境变量process.env.NODE_ENV
process.env.NODE_ENV它会根据你敲击的命令,来使用不同的值解决:
如果你敲击npm run serve, process.env .NODE_ENV的值就是'development'字符串
如果你敲击npm run build, process.env.NODE_ENV的值就是'prodution'字符串
publicPath: process.env.NODE_ENV === 'prodution' ? './' : '/'
经验二十一:
打包发布-减少包体积-排除第三方
1.先找到 `vue.config.js`, 添加`externals`项,具体如下
此选项作用, 告诉webpack排除掉这些包, 不进行打包
==一定要去修改掉引入Element用的变量名, 这里要匹配去替换==
==因为cdn里的源代码配置在ELEMENT这个变量上==
configureWebpack: {
// provide the app's title in webpack's name field, so that
// it can be accessed in index.html to inject the correct title.
// name: name,
externals: {
// 基本格式:
// '包名' : '在项目中引入的名字'
'echarts': 'echarts',
'vue': 'Vue',
'vue-router': 'VueRouter',
'vuex': 'Vuex',
'axios': 'axios',
'dayjs': 'dayjs',
'element-ui': 'ELEMENT',
'vue-quill-editor': 'VueQuillEditor',
'vuex-persistedstate': 'createPersistedState'
},
// resolve: {
// alias: {
// '@': resolve('src')
// }
//}
}
到了这里,关于黑马大事件项目经验的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!