Vue学习笔记
# 零、一些参考
-ElementUI动态校验 (opens new window)
# 一、Vue相关
# 1.1、图片资源
- 问题:
- 直接写死src=“http://....”可以使用
- 在data中定义变量imgUrl:“http://....”后使用:src="imgUrl"可以使用
- 直接vue语法赋值:src="scope.row.url"不起作用
在methods中添加函数getUrl,也可以注册成全局函数
methods: {
/** 解决图片资源不能直接使用问题 */
getUrl(row) {
return row.url
}
}
2
3
4
5
6
在使用的地方经过函数转换一下
<el-table-column label="子系统logo" align="center">
<template slot-scope="scope">
<el-popover
placement="left"
width="200"
trigger="hover">
<el-image
style="width: 100%; height: 100%"
:src="getUrl(scope.row)"
>
</el-image>
<el-button
size="mini"
type="text"
icon="el-icon-view"
slot="reference"
>查看logo</el-button>
</el-popover>
</template>
</el-table-column>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
如果使用的不是http开头,即你的图片没有上传图床、七牛云这样的地方,而是自己的服务器,存储又是其他相对路径那就用下面的处理:
methods: {
/** 解决图片资源不能直接使用问题 */
getUrl (row){
let logoUrl = row.url
if (!logoUrl.startsWith('http')) {
// 这里起作用的原因是框架层设置了代理
logoUrl = process.env.VUE_APP_BASE_API + logoUrl
}
return logoUrl
}
}
2
3
4
5
6
7
8
9
10
11
代理设置如下(vue.config.js) 这里的代理也是请求后台服务器的请求!
module.exports = {
// .... 省略无关配置
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
[process.env.VUE_APP_BASE_API]: { //拦截请求,并使用下方代理
target: `http://localhost:8080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
}
},
disableHostCheck: true
}
// .... 省略无关配置
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
图片加载不了时的默认图片
<el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image> <el-image>
<div slot="error" class="image-slot">
<i class="el-icon-picture-outline"></i>
</div>
</el-image>
2
3
4
5
6
7
8
9
代理下文还会有介绍,这里只说用法。
# 1.2、 编程式导航
编程式的导航router.push来实现界面切换,同时可以带参数
// 框架中的使用
this.$router.push("/tool/gen-edit/index/" + tableId);
// route中要有路由配置
{
path: '/tool/gen-edit',
component: Layout,
hidden: true,
children: [
{
path: 'index/:tableId(\\d+)',
component: (resolve) => require(['@/views/tool/gen/editTable'], resolve),
name: 'GenEdit',
meta: { title: '修改生成配置', activeMenu: '/tool/gen'}
}
]
}
// 带参数使用(刷新参数丢失)
this.$router.push({ name: 'news', params: { userId: 123 }})
//html中使用参数
{{this.$route.params.userId}}
// 带参数使用(刷新参数不丢失)
this.$router.push({ path: '/news', query: { userId: 123 }});
//html中使用参数
{{this.$route.query.userId}}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
注意:和name配对的是params,和path配对的是query
# 1.3、下拉列表使
<el-form-item label="所属系统" prop="subsysId">
<el-select
v-model="form.subsysId" <!--回显值-->
placeholder="选择所属系统"
style="width: 100%"
>
<el-option
v-for="subsys in subSysList"
:key="subsys.id"
:value="subsys.id"<!--和回显值一样的值,包括数据类型,如果v-model=1,:value="1"则不回关联下拉值中-->
:label="subsys.name"
/>
</el-select>
</el-form-item>
2
3
4
5
6
7
8
9
10
11
12
13
14
# 1.4、this.$nextTick()
this.$nextTick()将回调延迟到下次 DOM 更新循环之后执行。 在修改数据之后立即使用它,然后等待 DOM 更新。 它跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。 假设我们更改了某个dom元素内部的文本,而这时候我们想直接打印出这个被改变后的文本是需要dom更新之后才会实现的, 也就好比我们将打印输出的代码放在setTimeout(fn, 0)中;
# 1.5、this.$refs
- 案例一、ref 写在标签上时
<!-- ref 写在标签上时:this.$refs.名字 获取的是标签对应的dom元素
ref 写在组件上时:这时候获取到的是 子组件(比如counter)的引用-->
<div id="root">
<!-- ref = 'hello': 给div 起了一个引用的名字 hello -->
<div
ref = 'hello'
@click = "handleClick">
laugh yourself
</div>
</div>
<script>
var vm = new Vue({
el: '#root',
methods: {
handleClick: function() {
//this.$refs : 获取整个Vue实例中所有的引用 然后再找到hello这个引用 指向div那个dom节点
//console.log(this.$refs.hello)
alert(this.$refs.hello.innerHTML)
}
}
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 案例二、 ref 写在组件上时
<!-- ref 写在标签上时:this.$refs.名字 获取的是标签对应的dom元素
ref 写在组件上时:这时候获取到的是 子组件(比如counter)的引用-->
<div id="root">
<!-- 子组件触发了事件 这里父组件(模板区里面)监听该事件 调用handleChange方法
因此handleChange方法定义在父组件的methods里面-->
<counter ref="one" @change="handleChange"></counter>
<counter ref="two" @change="handleChange"></counter>
<div>{{total}}</div>
</div>
<script>
Vue.component('counter', {
template: '<div @click="handleClick"> {{number}} </div>',
data: function() {
return {
number: 0
}
},
methods: {
handleClick: function() {
this.number ++
//子组件向父组件传值 子组件被点击的时候 number+1 同时告诉外面 也即是触发一个事件
this.$emit('change')
}
},
})
var vm = new Vue({
el: '#root',
data: {
total: 0
},
methods: {
handleChange: function() {
//在此方法中计算两个数量的和 通过this.$refs.引用名字 获取两个子组件的引用
this.total = this.$refs.one.number +
this.$refs.two.number
}
}
})
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
# 1.6、代理
vue-ui是前后端分离项目,访问后台接口时,解决方案为代理。 借用代理的实现方式,可以为不同业务配置不同的接口代理。 实现方式如下:
- 1、配置环境变量,以开发开发环境为例子
- .env.development : 开发环境配置文件
- .env.production : 生产环境配置文件
- .env.staging : 测试环境配置文件
# 配置文件中配置多个业务接口的请求地址
# 业务零系统接口前缀
VUE_APP_BASE_API = '/prod-api'
# 业务一系统接口前缀
VUE_APP_ONE_BASE_API = '/one-prod-api'
2
3
4
5
6
- 2、设置代理
- vue.config.js : 配置文件
- 参考文件 (opens new window)
module.exports = {
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
// 业务零系统代理设置
[process.env.VUE_APP_BASE_API]: {
target: `http://localhost:8080`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_BASE_API]: ''
}
},
// 业务一系统代理设置
[process.env.VUE_APP_ONE_BASE_API]: {
target: `http://localhost:9090`,
changeOrigin: true,
pathRewrite: {
['^' + process.env.VUE_APP_ONE_BASE_API]: ''
}
}
},
disableHostCheck: true
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
- 3、业务请求接口处设置baseURL
- 各个封装的api.js文件
// 获取数据
export const getData = () => {
return request({
// 这里request是封装了的axios的请求方法,里面已经设置了baseURL,这里是进行覆盖
baseURL: process.env.VUE_APP_ONE_BASE_API,
url: '/getData',
method: 'get'
})
}
2
3
4
5
6
7
8
9
axios的封装部分代码
axios.defaults.headers['Content-Type'] = 'application/json;charset=utf-8'
// 创建axios实例
const service = axios.create({
// axios中请求配置有baseURL选项,表示请求URL公共部分
baseURL: process.env.VUE_APP_BASE_API,
// 超时
timeout: 10000
})
2
3
4
5
6
7
8
- ps: 代理的高级用法 先看代码
devServer: {
host: '0.0.0.0',
port: port,
open: true,
proxy: {
// detail: https://cli.vuejs.org/config/#devserver-proxy
'/': {
target: 'http://localhost',
changeOrigin: true,
pathRewrite: function (path, req) {
return path.replace(/&target.*/, '')
},
router: function (req) {
// 可以覆盖target,或者说可以根据url的格式等来判断代理的地址是哪里
return req.query.target
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 二、一些ES6用法
# 2.1 数组拼接 (opens new window)
// `b` onto `q`:
q.push.apply( q, b );
q; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]
// or `q` into `b`:
b.unshift.apply( b, q );
b; // [5,5,1,9,9,6,4,5,8,"tie","mao","csdn","ren","fu","fei"]
2
3
4
5
6
7
8
9
# 2.2 JS的 delete 操作符
const Employee = {
firstname: 'John',
lastname: 'Doe'
};
console.log(Employee.firstname);
// expected output: "John"
delete Employee.firstname;
console.log(Employee.firstname);
// expected output: undefined
2
3
4
5
6
7
8
9
10
11
12
# 2.3 for in 和for of的区别
- for in遍历的是数组的索引(即键名),而for of遍历的是数组元素值
- for of遍历的只是数组内的元素,而不包括数组的原型属性method和索引name
- 遍历对象:通常用for in来遍历对象的键名
- for..of适用遍历数/数组对象/字符串/map/set等拥有迭代器对象的集合.但是不能遍历对象,因为没有迭代器对象.与forEach()不同的是,它可以正确响应break、continue和return语句
- for-of循环不支持普通对象,但如果你想迭代一个对象的属性,你可以用for-in循环(这也是它的本职工作)或内建的Object.keys()方法
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
for (var key in myObject) {
console.log(key);
}
// 输入内容
a
b
c
method
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
for in 可以遍历到myObject的原型方法method,如果不想遍历原型方法和属性的话,可以在循环内部判断一下,hasOwnPropery方法可以判断某属性是否是该对象的实例属性
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
for (var key in myObject) {
if(myObject.hasOwnProperty(key)){
console.log(key);
}
}
// 输出内容
a
b
c
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
同样可以通过ES5的Object.keys(myObject)获取对象的实例属性组成的数组,包括原型方法和属性
Object.prototype.method=function(){
console.log(this);
}
var myObject={
a:1,
b:2,
c:3
}
// 这里用的是of的话不包含method
for (var key of Object.keys(myObject)) {
console.log(key + ": " + myObject[key]);
}
// 输出内容
a: 1
b: 2
c: 3
// 这里用的是in的话所有的值都会获得
for (var key in Object.keys(myObject)) {
console.log(key );
}
// 输出内容
1
2
3
method
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 2.4 时间格式化
使用Intl.DateTimeFormat (opens new window)
const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary
// Specify default date formatting for language (locale)
console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2020"
// Specify default date formatting for language with a fallback language (in this case Indonesian)
console.log(new Intl.DateTimeFormat(['ban', 'id']).format(date));
// expected output: "20/12/2020"
// Specify date and time format using "style" options (i.e. full, long, medium, short)
console.log(new Intl.DateTimeFormat('zh-CN', { dateStyle: 'full', timeStyle: 'medium' }).format(date));
// Expected output "2020年12月20日星期日 上午11:23:16"
2
3
4
5
6
7
8
9
10
11
12
13
14
# 3、el-upload上传组件
使用可以参考官网Api (opens new window),这里记录一下官网demo中但是api没有给出的说明。
# 3.1 Slot 插槽
官网给出的可用插槽有两个,trigger和tip,整理后如下:
表头 | 表头 |
---|---|
trigger | 触发文件选择框的内容 |
tip | 提示说明文字 |
default | 一般用来显示图标 |
file | 文件自定义操作,作用域插槽参数对应为当前文件slot-scope="{file}" |
实例如下:
<el-upload
action="#"
list-type="picture-card"
:auto-upload="false">
<i slot="default" class="el-icon-plus"></i>
<div slot="file" slot-scope="{file}">
<img
class="el-upload-list__item-thumbnail"
:src="file.url" alt=""
>
<span class="el-upload-list__item-actions">
<span
class="el-upload-list__item-preview"
@click="handlePictureCardPreview(file)"
>
<i class="el-icon-zoom-in"></i>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleDownload(file)"
>
<i class="el-icon-download"></i>
</span>
<span
v-if="!disabled"
class="el-upload-list__item-delete"
@click="handleRemove(file)"
>
<i class="el-icon-delete"></i>
</span>
</span>
</div>
</el-upload>
<el-dialog :visible.sync="dialogVisible">
<img width="100%" :src="dialogImageUrl" alt="">
</el-dialog>
<script>
export default {
data() {
return {
dialogImageUrl: '',
dialogVisible: false,
disabled: false
};
},
methods: {
handleRemove(file) {
console.log(file);
},
handlePictureCardPreview(file) {
this.dialogImageUrl = file.url;
this.dialogVisible = true;
},
handleDownload(file) {
console.log(file);
}
}
}
</script>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
为什么file插槽的作用域不是和原生的on-remove function(file, fileList)
这样的多个参数?源码如下:
<li
v-for="file in files"
:class="['el-upload-list__item', 'is-' + file.status, focusing ? 'focusing' : '']"
:key="file.uid"
tabindex="0"
@keydown.delete="!disabled && $emit('remove', file)"
@focus="focusing = true"
@blur="focusing = false"
@click="focusing = false"
>
<slot :file="file">
<img
class="el-upload-list__item-thumbnail"
v-if="file.status !== 'uploading' && ['picture-card', 'picture'].indexOf(listType) > -1"
:src="file.url" alt=""
>
....
render(h) {
let uploadList;
if (this.showFileList) {
uploadList = (
<UploadList
disabled={this.uploadDisabled}
listType={this.listType}
files={this.uploadFiles}
on-remove={this.handleRemove}
handlePreview={this.onPreview}>
{
(props) => {
if (this.$scopedSlots.file) {
return this.$scopedSlots.file({
file: props.file
});
}
}
}
</UploadList>
);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41