Vue学习笔记

2021/9/27 vueelementUI

# 零、一些参考

-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
    }
}
1
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>
1
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
  }
}
1
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
  }
  // .... 省略无关配置
1
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>
1
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}}
1
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>
1
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>
1
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>
1
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'
1
2
3
4
5
6
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
  }
}
1
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'
  })
}
1
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
})
1
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
        }
      }
    }
1
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"]
1
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
1
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
1
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
1
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
1
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"
1
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>
1
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>
      );
    }
1
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
留言:
    更新日期: 2022/2/8 下午11:10:51