20230627----重返学习-全局处理vue-Vue.use()-element-ui说明-vue2中原型链-如何设置样式

news/2025/1/11 18:44:41/

day-100-one hundred-20230627-全局处理vue-Vue.use()-element-ui说明-vue2中原型链-如何设置样式

全局处理vue

  1. 创建/src/global.js,用于设置全局相关的配置。

    • fang/f20230627/day0627/src/global.js

      import Vue from "vue";// [vue-全局配置](https://v2.cn.vuejs.org/v2/api/#全局配置)
      //[取消生产环境下的Vue提示信息](https://v2.cn.vuejs.org/v2/api/#productionTip)。
      Vue.config.productionTip = false;// [vuejs-devtools 的配置说明](https://v2.cn.vuejs.org/v2/api/#devtools)
      //[vuejs-devtools插件](https://chrome.google.com/webstore/detail/vuejs-devtools/nhdogjmejiglipccpnnnanhbledajbpd)// [Vue-use](https://v2.cn.vuejs.org/v2/api/#Vue-use)
      // Vue.use(function install(_Vue){
      //   console.log(`_Vue===Vue-->`, _Vue===Vue);
      //   //然后在该函数内部想怎么处理,就是自己的事了!
      // })// // [Vue-use](https://v2.cn.vuejs.org/v2/api/#Vue-use)
      // Vue.use({
      //   install(_Vue) {
      //     console.log(`对象:_Vue===Vue-->`, _Vue === Vue);
      //     //然后在该函数内部想怎么处理,就是自己的事了!
      //   },
      // });/* // 全局配置element-ui。
      // 导入element-ui
      import "element-ui/lib/theme-chalk/index.css";
      import ElementUI from "element-ui";
      Vue.use(ElementUI); */import "element-ui/lib/theme-chalk/index.css";
      import { Button, Tag, Loading, Message } from "element-ui";
      Vue.use(Button).use(Tag).use(Loading.directive);
      Vue.prototype.$message = Message;
      
  2. /src/main.js中,在根视图/src/App.vue导入之前先导入。

    • fang/f20230627/day0627/src/main.js

      import Vue from 'vue'
      import './global'
      import App from './App.vue'
      

vuejs-devtools

  1. vuejs-devtools 的配置说明
  2. vuejs-devtools插件

Vue.use()

  • Vue.use() 是Vue2中使用/注册插件的方式。
    1. Vue-use
    • 可以传递对象或者函数。
      • 传递对象:要求对象必须具备install的成员,成员值是一个函数,当Vue.use处理的时候,会自动执行install方法,并且把Vue传递进去。

      • 返回的依旧是Vue对象,所以可以实现链式写法。

        import Vue from "vue";// [Vue-use](https://v2.cn.vuejs.org/v2/api/#Vue-use)
        Vue.use({install(_Vue) {console.log(`对象:_Vue===Vue-->`, _Vue === Vue);//然后在该函数内部想怎么处理,就是自己的事了!},
        });
        
      • 传递函数:函数本身就是install方法,会被触发执行,传递Vue!

        import Vue from "vue";// [Vue-use](https://v2.cn.vuejs.org/v2/api/#Vue-use)
        Vue.use(function install(_Vue){console.log(`_Vue===Vue-->`, _Vue===Vue);//然后在该函数内部想怎么处理,就是自己的事了!
        })
        
        • 然后在该函数内部想怎么处理,就是自己的事了!
          • 也就是说,可以对Vue原型对象设置一些原型方法,也可以设置一些自定义指令或自定义组件。

element-ui说明

  1. fang/f20230627/day0627/node_modules/element-ui/src/index.js中可以看到element-ui的源码入口。

  2. 全局导入:在全局配置中使用通过Vue.use()后,会调用它的element-ui中的install方法,而install方法中主要执行了:

    • fang/f20230627/day0627/src/global.js

      import ElementUI from "element-ui"
      import 'element-ui/lib/theme-chalk/index.css'Vue.use(ElementUI)
      
      • 这个实际上,是执行了/node_modules/element-ui/lib/index.js。而/node_modules/element-ui/lib/index.js,这个在网上查和问人,得知是由/node_modules/element-ui/src/index.js打包而来。
    • fang/f20230627/day0627/node_modules/element-ui/src/index.js

      // Vue.use后调用该方法。
      const install = function(Vue, opts = {}) {// 设置element-ui的国际化处理。locale.use(opts.locale);locale.i18n(opts.i18n);// 注册85个全局组件。components.forEach(component => {Vue.component(component.name, component);});Vue.use(InfiniteScroll);// 注册一个v-InfiniteScroll自定义指令,用于虚拟滚动。fang/f20230627/day0627/node_modules/element-ui/packages/infinite-scroll/index.js。v-loading自定义指令。Vue.use(Loading.directive);// 注册// 向Vue的原理上挂载一个$ELEMENT配置项,这个配置项是element-ui的全局配置。Vue.prototype.$ELEMENT = {size: opts.size || '',zIndex: opts.zIndex || 2000};// 还在Vue的原型上挂载很多消息提示的方法,组件中基于this.$xxx直接调用就可以了。Vue.prototype.$loading = Loading.service;Vue.prototype.$msgbox = MessageBox;Vue.prototype.$alert = MessageBox.alert;Vue.prototype.$confirm = MessageBox.confirm;Vue.prototype.$prompt = MessageBox.prompt;Vue.prototype.$notify = Notification;Vue.prototype.$message = Message;};
      
      /* Automatically generated by './build/bin/build-entry.js' */import Pagination from '../packages/pagination/index.js';
      import Dialog from '../packages/dialog/index.js';
      import Autocomplete from '../packages/autocomplete/index.js';
      import Dropdown from '../packages/dropdown/index.js';
      import DropdownMenu from '../packages/dropdown-menu/index.js';
      import DropdownItem from '../packages/dropdown-item/index.js';
      import Menu from '../packages/menu/index.js';
      import Submenu from '../packages/submenu/index.js';
      import MenuItem from '../packages/menu-item/index.js';
      import MenuItemGroup from '../packages/menu-item-group/index.js';
      import Input from '../packages/input/index.js';
      import InputNumber from '../packages/input-number/index.js';
      import Radio from '../packages/radio/index.js';
      import RadioGroup from '../packages/radio-group/index.js';
      import RadioButton from '../packages/radio-button/index.js';
      import Checkbox from '../packages/checkbox/index.js';
      import CheckboxButton from '../packages/checkbox-button/index.js';
      import CheckboxGroup from '../packages/checkbox-group/index.js';
      import Switch from '../packages/switch/index.js';
      import Select from '../packages/select/index.js';
      import Option from '../packages/option/index.js';
      import OptionGroup from '../packages/option-group/index.js';
      import Button from '../packages/button/index.js';
      import ButtonGroup from '../packages/button-group/index.js';
      import Table from '../packages/table/index.js';
      import TableColumn from '../packages/table-column/index.js';
      import DatePicker from '../packages/date-picker/index.js';
      import TimeSelect from '../packages/time-select/index.js';
      import TimePicker from '../packages/time-picker/index.js';
      import Popover from '../packages/popover/index.js';
      import Tooltip from '../packages/tooltip/index.js';
      import MessageBox from '../packages/message-box/index.js';
      import Breadcrumb from '../packages/breadcrumb/index.js';
      import BreadcrumbItem from '../packages/breadcrumb-item/index.js';
      import Form from '../packages/form/index.js';
      import FormItem from '../packages/form-item/index.js';
      import Tabs from '../packages/tabs/index.js';
      import TabPane from '../packages/tab-pane/index.js';
      import Tag from '../packages/tag/index.js';
      import Tree from '../packages/tree/index.js';
      import Alert from '../packages/alert/index.js';
      import Notification from '../packages/notification/index.js';
      import Slider from '../packages/slider/index.js';
      import Loading from '../packages/loading/index.js';
      import Icon from '../packages/icon/index.js';
      import Row from '../packages/row/index.js';
      import Col from '../packages/col/index.js';
      import Upload from '../packages/upload/index.js';
      import Progress from '../packages/progress/index.js';
      import Spinner from '../packages/spinner/index.js';
      import Message from '../packages/message/index.js';
      import Badge from '../packages/badge/index.js';
      import Card from '../packages/card/index.js';
      import Rate from '../packages/rate/index.js';
      import Steps from '../packages/steps/index.js';
      import Step from '../packages/step/index.js';
      import Carousel from '../packages/carousel/index.js';
      import Scrollbar from '../packages/scrollbar/index.js';
      import CarouselItem from '../packages/carousel-item/index.js';
      import Collapse from '../packages/collapse/index.js';
      import CollapseItem from '../packages/collapse-item/index.js';
      import Cascader from '../packages/cascader/index.js';
      import ColorPicker from '../packages/color-picker/index.js';
      import Transfer from '../packages/transfer/index.js';
      import Container from '../packages/container/index.js';
      import Header from '../packages/header/index.js';
      import Aside from '../packages/aside/index.js';
      import Main from '../packages/main/index.js';
      import Footer from '../packages/footer/index.js';
      import Timeline from '../packages/timeline/index.js';
      import TimelineItem from '../packages/timeline-item/index.js';
      import Link from '../packages/link/index.js';
      import Divider from '../packages/divider/index.js';
      import Image from '../packages/image/index.js';
      import Calendar from '../packages/calendar/index.js';
      import Backtop from '../packages/backtop/index.js';
      import InfiniteScroll from '../packages/infinite-scroll/index.js';
      import PageHeader from '../packages/page-header/index.js';
      import CascaderPanel from '../packages/cascader-panel/index.js';
      import Avatar from '../packages/avatar/index.js';
      import Drawer from '../packages/drawer/index.js';
      import Statistic from '../packages/statistic/index.js';
      import Popconfirm from '../packages/popconfirm/index.js';
      import Skeleton from '../packages/skeleton/index.js';
      import SkeletonItem from '../packages/skeleton-item/index.js';
      import Empty from '../packages/empty/index.js';
      import Descriptions from '../packages/descriptions/index.js';
      import DescriptionsItem from '../packages/descriptions-item/index.js';
      import Result from '../packages/result/index.js';
      import locale from 'element-ui/src/locale';
      import CollapseTransition from 'element-ui/src/transitions/collapse-transition';const components = [Pagination,Dialog,Autocomplete,Dropdown,DropdownMenu,DropdownItem,Menu,Submenu,MenuItem,MenuItemGroup,Input,InputNumber,Radio,RadioGroup,RadioButton,Checkbox,CheckboxButton,CheckboxGroup,Switch,Select,Option,OptionGroup,Button,ButtonGroup,Table,TableColumn,DatePicker,TimeSelect,TimePicker,Popover,Tooltip,Breadcrumb,BreadcrumbItem,Form,FormItem,Tabs,TabPane,Tag,Tree,Alert,Slider,Icon,Row,Col,Upload,Progress,Spinner,Badge,Card,Rate,Steps,Step,Carousel,Scrollbar,CarouselItem,Collapse,CollapseItem,Cascader,ColorPicker,Transfer,Container,Header,Aside,Main,Footer,Timeline,TimelineItem,Link,Divider,Image,Calendar,Backtop,PageHeader,CascaderPanel,Avatar,Drawer,Statistic,Popconfirm,Skeleton,SkeletonItem,Empty,Descriptions,DescriptionsItem,Result,CollapseTransition
      ];// Vue.use后调用该方法。
      const install = function(Vue, opts = {}) {// 设置element-ui的国际化处理。locale.use(opts.locale);locale.i18n(opts.i18n);// 注册85个全局组件。components.forEach(component => {Vue.component(component.name, component);});Vue.use(InfiniteScroll);// 注册一个v-InfiniteScroll自定义指令,用于虚拟滚动。fang/f20230627/day0627/node_modules/element-ui/packages/infinite-scroll/index.js。v-loading自定义指令。Vue.use(Loading.directive);// 注册// 向Vue的原理上挂载一个$ELEMENT配置项,这个配置项是element-ui的全局配置。Vue.prototype.$ELEMENT = {size: opts.size || '',zIndex: opts.zIndex || 2000};// 还在Vue的原型上挂载很多消息提示的方法,组件中基于this.$xxx直接调用就可以了。Vue.prototype.$loading = Loading.service;Vue.prototype.$msgbox = MessageBox;Vue.prototype.$alert = MessageBox.alert;Vue.prototype.$confirm = MessageBox.confirm;Vue.prototype.$prompt = MessageBox.prompt;Vue.prototype.$notify = Notification;Vue.prototype.$message = Message;};/* istanbul ignore if */
      if (typeof window !== 'undefined' && window.Vue) {install(window.Vue);
      }export default {version: '2.15.13',locale: locale.use,i18n: locale.i18n,install,CollapseTransition,Loading,Pagination,Dialog,Autocomplete,Dropdown,DropdownMenu,DropdownItem,Menu,Submenu,MenuItem,MenuItemGroup,Input,InputNumber,Radio,RadioGroup,RadioButton,Checkbox,CheckboxButton,CheckboxGroup,Switch,Select,Option,OptionGroup,Button,ButtonGroup,Table,TableColumn,DatePicker,TimeSelect,TimePicker,Popover,Tooltip,MessageBox,Breadcrumb,BreadcrumbItem,Form,FormItem,Tabs,TabPane,Tag,Tree,Alert,Notification,Slider,Icon,Row,Col,Upload,Progress,Spinner,Message,Badge,Card,Rate,Steps,Step,Carousel,Scrollbar,CarouselItem,Collapse,CollapseItem,Cascader,ColorPicker,Transfer,Container,Header,Aside,Main,Footer,Timeline,TimelineItem,Link,Divider,Image,Calendar,Backtop,InfiniteScroll,PageHeader,CascaderPanel,Avatar,Drawer,Statistic,Popconfirm,Skeleton,SkeletonItem,Empty,Descriptions,DescriptionsItem,Result
      };
      
    • 关于全局引入:

      1. 如果项目比较大,比如80个组件用到了40多个,就全局导入。
      2. 如果项目比较少,80个组件,才使用10多个,就按需导入。
    • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tZy3y1dg-1687881808375)(./Vue.use(ElementUI)]后执行的事.jpg)

  3. 按需导入:

    • 实现组件库的按需导入:

      1. 安装插件

        $ yarn add babel-plugin-component -D
        babel-plugin-component 是 element 对 babel-plugin-import 的重写
        
        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jwAtmvLW-1687881808378)(./element-ui的按需导入.jpg)]
      2. 按需导入需要的组件等,并且需要把组件注册为全局组件

        import { Button, Tag } from 'element-ui'
        Vue.component(Button.name, Button)
        Vue.component(Tag.name, Tag)
        
        • 但是这样做有点麻烦;

          • 通过看element-ui具体组件的源码,可以用Vue.use()给注册。

            import "element-ui/lib/theme-chalk/index.css";//element-ui的样式表,好像不能按需导入。所以要全部导入。
            import { Button, Tag, Loading, Message } from "element-ui";
            Vue.use(Button).use(Tag).use(Loading.directive);
            Vue.prototype.$message = Message;
            
        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ta1Q26kS-1687881808379)(./element-ui组件的按需导入.jpg)]

        • element-ui的样式表,好像不能按需导入。所以要全部导入。

      • 看element-ui的样式,可以看到一些具体的UI组件是在/node_modules/element-ui/packages这个目录中。
  4. 局部组件中调用全局注册的el-button时。

    <template><div class="demo-box"><!-- 因为el-button是一个组件,所以@click="buttonHandle"并不是基于addEventListener给元素做事件绑定,而是给el-button组件的事件池中,注入一个click的自定义事件,方法是buttonHandle。- buttonHandle方法是否可以执行,需要在el-button组件内部,基于this.$emit()处理。- 分析el-button的源码发现:- 调用这个组件,最后组件渲染出来的是一个button标签。- 默认给button标签做了click事件绑定,点击按钮执行组件内部的handleClick方法。- 在handleClick方法中,把刚才注册的click自定义事件,以及绑定的buttonHandle方法,基于$emit()通知执行。- @mouseenter="buttonHandle"操作- 这个操作也是给组件的事件池中,注入一个mouseenter的自定义事件,方法是buttonHandle。- 但可惜的是,通过对el-button源码的分析,我们发现,其内部没有对mouseenter自定义事件做处理,所以即便鼠标进入按钮区域,也不会有任何的效果。--><el-button type="primary" @click="buttonHandle" @mouseenter="buttonHandle">element按钮</el-button></div>
    </template><script>
    export default {methods: {buttonHandle(){this.$message.success('哇哇叫')}}
    };
    </script>
    
    • 因为el-button是一个组件,所以@click="buttonHandle"并不是基于addEventListener给元素做事件绑定,而是给el-button组件的事件池中,注入一个click的自定义事件,方法是buttonHandle。
      • buttonHandle方法是否可以执行,需要在el-button组件内部,基于this.$emit()处理。
        • 分析el-button的源码发现:
          1. 源码在:
            • fang/f20230627/day0627/node_modules/element-ui/packages/button/index.js

              import ElButton from './src/button';/* istanbul ignore next */
              ElButton.install = function(Vue) {Vue.component(ElButton.name, ElButton);
              };export default ElButton;
              
            • fang/f20230627/day0627/node_modules/element-ui/packages/button/src/button.vue

<template><buttonclass="el-button"@click="handleClick":disabled="buttonDisabled || loading":autofocus="autofocus":type="nativeType":class="[type ? 'el-button--' + type : '',buttonSize ? 'el-button--' + buttonSize : '',{'is-disabled': buttonDisabled,'is-loading': loading,'is-plain': plain,'is-round': round,'is-circle': circle}]"><i class="el-icon-loading" v-if="loading"></i><i :class="icon" v-if="icon && !loading"></i><span v-if="$slots.default"><slot></slot></span></button>
</template>
<script>export default {name: 'ElButton',inject: {elForm: {default: ''},elFormItem: {default: ''}},props: {type: {type: String,default: 'default'},size: String,icon: {type: String,default: ''},nativeType: {type: String,default: 'button'},loading: Boolean,disabled: Boolean,plain: Boolean,autofocus: Boolean,round: Boolean,circle: Boolean},computed: {_elFormItemSize() {return (this.elFormItem || {}).elFormItemSize;},buttonSize() {return this.size || this._elFormItemSize || (this.$ELEMENT || {}).size;},buttonDisabled() {return this.$options.propsData.hasOwnProperty('disabled') ? this.disabled : (this.elForm || {}).disabled;}},methods: {handleClick(evt) {this.$emit('click', evt);}}};
</script>
        - 调用这个组件,最后组件渲染出来的是一个button标签。- 默认给button标签做了click事件绑定,点击按钮执行组件内部的handleClick方法。- 在handleClick方法中,把刚才注册的click自定义事件,以及绑定的buttonHandle方法,基于$emit()通知执行。- @mouseenter="buttonHandle"操作- 这个操作也是给组件的事件池中,注入一个mouseenter的自定义事件,方法是buttonHandle。- 但可惜的是,通过对el-button源码的分析,我们发现,其内部没有对mouseenter自定义事件做处理,所以即便鼠标进入按钮区域,也不会有任何的效果。- 但可以通过`@vue原生支持事件.native`来让组件的根视图节点中强制注入一个对应的原生事件。```vue<template><div class="demo-box"><el-button type="primary" @click="buttonHandle" @mouseenter.native="buttonHandle">element按钮</el-button><el-tag>标签</el-tag></div></template><script>export default {methods: {buttonHandle(){this.$message.success('哇哇叫')}}};</script><style></style>```- 可以看到,移动到el-button后,可以触发绑定的事件了。即便el-button内部并没有触发mouseenter自定义事件。这个是因为,buttonHandle实际上是绑定在el-button根节点上的mouseenter事件中的。

修饰符.native的作用

  • 面试题:修饰符 .native 的作用
    • 在我之前的项目中,遇到过这样的需求:当我调用其它组件(比如UI组件库中的组件),基于v-on(@符)绑定事件和方法,此操作并不是给元素做事件绑定,而是往组件的事件池中注入自定义事件和方法,此时就需要在组件内部,对这个自定义事件,基于$emit()做处理!
    • 如果在组件中没有相关的处理,则我们注入的自定义事件是没有用的;而UI组件库中的源码我们是无法更改的,我需要的一些事件需求,组件库并不支持。
      • 比如:el-tag等组件中并不支持mouseenter/mouseover。
    • 此时我们就需要使用.native修饰符,设置此修饰符后:
      • 会强制给组件的根元素,完成对应的事件绑定。当事件触发,会把我们绑定的方法执行。
        • 只不过这些事件必须是浏览器标准事件。
        • [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bDiV7G07-1687882579600)(./组件内部事件通过事件传播传播到组件根节点.jpg)]
          1. 组件内部的元素的事件可以通过事件传播传播到组件的根节点上。所以可以在组件根节点对事件进行事件委托,监听到具体的元素所触发的事件。
    • 而之前的项目中,尤其是调用UI组件库中的组件,.native修饰符用的还是很多的!

父组件传递给子组件的属性

  • 代码示例:
    • 父组件
      • fang/f20230627/day0627/src/views/Demo2.vue
    • 子组件
      • fang/f20230627/day0627/src/views/Child.vue
  • 组件说明
    • 针对父组件传递进来的属性:

      • class/style: 不能被props注册接收,也不会在$attrs中,只会直接设置在子组件的根元素上。

        <template><div class="child-box">子组件</div>
        </template><script>
        export default {props: ["class", "style"],created() {console.log(`this-->`, this);},
        };
        </script>
        
      • 可以基于props注册接收(细节如下面所示。)

        • 基于props注册接收的属性
          • 都会直接挂载到实例上,而且进行了响应式劫持。

          • 被注册的属性,不会出现在根元素上,也不会出现在$attrs中。

          • 注册接收的时候,还可以对属性进行规则校验。

            <template><div class="child-box">子组件</div>
            </template><script>
            export default {props: {x: { type: [Number, String] },title: {type: String,required: true,},bool: {type: Boolean,default: false,},info: Object,},created() {console.log(`this-->`, this);},
            };
            </script>
            
            • 如果传递的属性值,不符合校验规则,控制台会抛出警告错误,但是不影响属性的获取和视图的渲染。
              • vue2-props校验官方文档
      • 没有被props注册接收的属性,可以在$attrs中获取,也可以在子组件的根元素上看到。

    • 对于传递的属性,如果不想让其出现在根元素上(排除class/style)?

      • 基于props注册接收即可。

      • 也可以设置inheritAttrs: false

        <template><div class="child-box">子组件</div>
        </template><script>
        export default {inheritAttrs: false,created() {console.log(`this-->`, this);},
        };
        </script>
        
    • 针对于父组件传递的自定义事件

      • 基于$listeners获取子组件事件池中,所有的自定义事件和绑定的方法。

        <template><div class="child-box">子组件</div>
        </template><script>
        export default {created() {console.log(`this.$listeners-->`, this.$listeners);},
        };
        </script>
        
      • 基于$emit()通知自定义事件执行,传递实参信息。

        • this.$emit(‘自定义事件名’,要传的参数) => 返回值是子组件的实例

          • 如:this.$emit(‘close’,100)

            <script>
            export default {created() {console.log(`this-->`, this);let res = this.$emit("close", 10);console.log(`子组件中,接收父组件handle执行的返回结果。res-->`, res); //组件实例。},
            };
            </script>
            
        • this.$listeners.‘自定义事件名’ => 返回值是父组件对应方法执行的返回结果

          • 如:this.$listeners.close(10)

            <script>
            export default {created() {console.log(`this-->`, this);let res = this.$listeners.close(10)console.log(`子组件中,接收父组件handle执行的返回结果。res-->`, res);//父组件方法的返回值-'return-fang'。},
            };
            </script>
            
      • 各应用场景:

        1. 如果需要用到父组件中对应方法执行的返回结果,一般用this.$listeners.['自定义事件名'](要传的参数)
        2. 其它场景,一般都统一使用this.$emit('自定义事件名',要传的参数)

vue2中原型链

  • 在Vue2中,我们创建的 单文件组件,默认都是 ”类组件“「每个组件都相当于一个类」,调用此组件,相当于创建这个组件类的一个实例

    • 实例.proto —> VueComponent.prototype —> Vue.prtototype
  • 总结:每一次调用组件,都相当于创建 Vue 类的一个实例,都可以基于原型链,找到Vue.prototype「组件内部的this就是实例,可以访问到Vue.prototype上的信息」

  • Vue2中组件的分类:

    • 主流分类模式:
      • 全局组件: 基于 Vue.component 注册,一但注册为全局组件,可以在其它任意组件中,直接调用!「真实项目中,通用组件(比如UI组件库中的组件),一般会被注册为全局组件」
        1. 全局组件不能太多,否则容易引发命名冲突。
        2. 太少的话,常引用的地方都要写一遍导入与注册与流程的流程,导致代码变多。如果为全局组件,则只需要调用即可。
      • 局部/私有组件:在其它组件中,调用它的时候,需要 1导入、2注册、3调用。
    • 还有一种分类模式:
      • 类组件:每创建一个组件,都相当于创建一个类(内部是基于Vue.extend构建的),调用组件相当于创建类的一个实例(this),而且实例可以基于原型链找到 Vue.prototype!
      • 函数组件:创建组件就相当于创建一个函数,调用组件仅仅是把函数执行,这样没有实例(this)这些东西了!! ===> functional

v-for和v-if的优先级问题

  • 面试题:v-for和v-if的优先级问题

    <template><div class="demo-box"><div v-for="(item, index) in arr" :key="item" v-if="index % 2 === 0">{{ item }}</div></div>
    </template><script>
    export default {data() {return {arr: [10, 20, 30, 40],};},
    };
    </script>
    
    • 在vue2中,v-for的优先级要高于v-if,这样在循环的时候,v-for会先创建元素,再经过v-if的条件判断,如果结果是false,再把创建的元素销毁。DOM元素的一创一销之间,会造成性能的浪费!

    • 在vue3中,v-if的优先级要高于v-for,这样在v-if中,是无法使用v-for循环的内容的。

      • 如item及index的,会报错。
    • 总之:不论是vue2还是vue3中,都不要把v-for和v-if作用在相同的元素上。如果有类似的需求,可以基于<template>标签,把v-for和v-if分开处理!

      <template><div class="demo-box"><!-- `<template>标签`上使用v-for,是不能设置key的。 --><template v-for="(item, index) in arr"><div :key="item" v-if="index % 2 === 0">{{ item }}</div></template></div>
      </template><script>
      export default {data() {return {arr: [10, 20, 30, 40],};},
      };
      </script>
      
      1. <template>标签上使用v-for,是不能设置key的。
      2. key可以不设置,也可以设置。在一些语法检测工具中,会标红。不过不设置也没什么关系。如果要设置key,key要在<template>标签内部的根节点上加。

v-html的安全问题

  • 面试题:v-html的安全问题。

    1. 有时从服务器拿到的内容是后端生成的,有结构和样式。如:<a href="#">方一</a><button>呵</button>一
      • 直接渲染,样式不太好

        <template><div class="demo-box" >{{ body }}</div>
        </template><script>
        export default {data() {return {body: `<a href="#">方一</a><button>呵</button>一`,};},
        };
        </script>
        
        <template><div class="demo-box" v-text="body"></div>
        </template><script>
        export default {data() {return {body: `<a href="#">方一</a><button>呵</button>一`,};},
        };
        </script>
        
    • vue-dompurify-html。

    • vue-html-sanitizer。

    • 回答:

      • 在我之前的项目开发中,内容的渲染我基本上都是基于小胡子语法来处理的。

        • 但是此语法也有一个不足:如果渲染的内容包含html标签字符串,最后渲染的结果全部会作为普通字符串,无法把其识别为真正的html标签(类似于innerText)。
          • 此时我们就需要基于v-html指令来渲染,这个指令可以把渲染内容中的html标签字符串,变为真正的html标签(类似于innerHTML)。

            <template><div class="demo-box" v-html="body"></div>
            </template><script>
            export default {data() {return {body: `<a href="#">方一</a><button>呵</button>一`,};},
            };
            </script>
            
      • 但是v-html的使用上需要慎重,官方有这样的提示:在网站上动态渲染任意 HTML 是非常危险的,因为容易导致 XSS 攻击(XSS:跨站脚本攻击,有网站上注入恶意的客户端代码)。

        <template><div class="demo-box" v-html="body"></div>
        </template><script>
        export default {data() {return {body: `<a href="#">方一</a><button onClick="alert('我是button中的病毒,将清空页面!');document.body.innerHTML=''">呵</button>一<script>alert('我是script中的病毒');console.log('5555')<\/script>`,};},
        };
        </script>
        
        • 只在可信内容上使用 v-html,永远不要用在用户提交的内容上。即v-html中的要渲染的内容,不能是用户的代码。
          1. 用户基于富文本编辑器,编辑好内容后,提交给服务器的内容:包含html、样式、内容的字符串。
        • 如果需要渲染的信息,是用户提交的内容,则要慎重再慎重!
      • 如果必须要渲染这样的内容,则我们在渲染之前,需要对内容中,容易导致XSS攻击的标签,进行过滤处理(比如过滤掉<script><iframe>、一些js脚本等等)。

        • 如果自己处理,则使用正则匹配解析…处理起来具备一定的难度,而此时我们会使用一些插件来解决。
        • 推荐:vue-dompurify-html。
        • 推荐:vue-html-sanitizer。
      • 还可以在用户提交信息的时候,对提交的内容直接做安全校验,但凡包含一些有安全隐患的内容,就不允许提交了!其实后端一般也会做校验,防止用户直接绕开浏览器根据接口把有问题内容给提交。

如何设置样式

  • 面试题:如何设置样式「面试题:Class 与 Style 如何动态绑定?」
    • class动态绑定

      • 对象方式:

        <template><!-- 基于对象的方式来管理 --><div:class="{'demo-box': true,active: bool,}"@click="bool = !bool"></div>
        </template><script>
        export default {data() {return {bool: true,};},
        };
        </script>
        <style lang="less" scoped>
        .demo-box {width: 100px;height: 100px;background: pink;&.active {border: 5px solid red;}
        }
        </style>
        
      • 数组方式:

        <template><!-- 基于数组的方式来管理 --><div :class="['demo-box', bool ? 'active' : '']" @click="bool = !bool"></div>
        </template><script>
        export default {data() {return {bool: true,act: "active",};},
        };
        </script>
        <style lang="less" scoped>
        .demo-box {width: 100px;height: 100px;background: pink;&.active {border: 5px solid red;}
        }
        </style>
        
        • 数组样式的场景:

          <template><div :class="['demo-box', act]" @click="bool = !bool"></div>
          </template><script>
          export default {data() {return {act: "active",//通过控制一个变量的值来控制一个样式类名,适用于数组。};},
          };
          </script>
          
    • style动态绑定:

      • 对象方式:

        <template><div@click="bool = !bool":style="{width: '100px',height: '100px',background: 'pink',border: bool ? '5px solid red' : '',}"></div>
        </template><script>
        export default {data() {return {bool: true,};},
        };
        </script>
        
      • 数组方式:

        <template><div :style="[aa, bb]">哈哈</div>
        </template><script>
        export default {data() {return {aa: { color: "red" },bb: {fontSize: "20px",},};},
        };
        </script>
        

样式私有化方案

  • 面试题:样式私有化方案「面试题:在 Vue 组件 <style lang='less' scoped> 中编写的样式没有生效,都可能存在哪些原因?以及该如何解决?」
  • 样式私有化方案:
    • 无样式私有化:给<style>标签设置scoped属性;

      <template><div class="demo-box"><h2 class="title">哈哈哈哈哈哈哈哈</h2><div class="con"><el-button type="primary" icon="el-icon-message">el-button按钮</el-button></div></div>
      </template><script>
      export default {data() {return {};},
      };
      </script>
      <style lang="less">
      .demo-box{}
      </style>
      
    • 有了样式私有化:

      <template><div class="demo-box"><h2 class="title">哈哈哈哈哈哈哈哈</h2><div class="con"><el-button type="primary" icon="el-icon-message">el-button按钮</el-button></div></div>
      </template><script>
      export default {data() {return {};},
      };
      </script>
      <style lang="less" scoped>
      .demo-box{}
      </style>
      
  1. scoped样式私有化实现的原理

    • 在没有设置scoped属性之前:

      • 渲染的标签没有什么特殊处理。
      • 编写的样式都是全局样式。
    • 这样在组件合并渲染的时候,如果两个组件设置了相同的样式类名,就很可能发生样式冲突!

      • 在不使用任何技术方案的情况下,我们完全可以依托于命名规范,来解决样式冲突问题:

        1. 让每个组件最外层样式类名是唯一的。

          • 比如以路径+组件名+后缀,作为样式类的命名规范。

            .home-demo-box{...}
            
        2. 其内部元素的样式,都设置在这个唯一的样式类名下。

          .home-demo-box{.link{...}
          }
          
        • 但是此类方案,需要所有开发者,严格遵照此规范进行处理!
    • 但vue中提供了专门的样式私有化处理方案,即style-scoped。一旦给<style>标签设置scoped属性

      1. 每创建一个单文件组件,该组件都有一个唯一的id值,比如:data-v-fecc192e

      2. 当设置scoped属性之后,组件视图中出现的所有元素(包括调用的子组件的根元素-但不包括子组件内部元素),都会设置一个属性:组件id。

        <div data-v-fecc192e class="demo-box">
        

        实际的值:

        <div data-v-fecc192e="" class="demo-box">
        
      3. 并且我们在<style>标签中编写的样式,都被加上一个属性选择器。

        .demo-box[data-v-fecc192e]{...}
        
        • 这样即便编写的样式还是全局样式,但是因为设置了属性选择器,只有拥有相同属性的元素,此样式才会对其生效!
      • 这就是Vue样式私有化的原理!
    • 代码示例:

      <template><div class="demo-box"><h2 class="title">哈哈哈哈哈哈哈哈</h2><div class="con"><el-button type="primary" icon="el-icon-message">el-button按钮</el-button></div></div>
      </template><script>
      export default {data() {return {};},
      };
      </script>
      <style lang="less" scoped>
      .demo-box {box-sizing: border-box;background: pink;.title{font-size: 20px;}.el-button{border-radius: 0;}
      }
      </style>
      
  2. 但是此类方案也存在弊端:调用子组件的内部元素(除根元素),是没有设置组件id的这个属性的,如果在style中,给这些元素写样式,编写的样式是不生效的!

    • 原因:编写的样式有属性选择器,但是结构上没有相关的属性。

    • 解决方案:把编写样式中的属性选择器去掉即可,此时需要用到:deep()样式穿透!

      :deep(.el-icon-message) {...}
      
      /deep/ .el-icon-message {...}
      
      • 所谓样式穿透,就是把我们写的样式中的属性选择器干掉!
        • 我们一般都把这样的样式,写在某个私有化样式的底下。

样式私有化后的问题

  • 面试题:在 Vue 组件 <style lang='less' scoped> 中编写的样式没有生效,都可能存在哪些原因?以及该如何解决?
    • 从我之前的开发经验来讲,可能有两种情况:
      • 编写的样式权重不够,此时为了图简单,可以很暴力地使用!important,但是建议还是用其它方式调节权重即可!
      • 最常见的情况,就是给子组件内部元素编写样式,因为元素不会设置组件唯一id这个属性,但是我们写的样式会设置上这样的属性选择器,所以样式不生效;此时我们基于:deep穿透一下即可!
      • 当然有时候也是因为项目太急,加班时间有点多,眼花写错了!

css模块私有化方案

  • Vue中还提供了一种样式私有化方案:CSS Modules;
  • 具体步骤:
    1. 创建一个文件名.module.css的文件。
      1. 这个也可以不用.module.css为后缀,需要到在/vue.config.js中进行配置。
      2. 好像也可以不用css,而是使用less文件。
    2. 在vue文件中以模块方式导入该css文件。
    3. 将该css文件模块存储为变量。
    4. <template>标签中,使用:class="css文件模块名['类名']"的方式来设置对应的DOM元素。
  • 代码:
    • fang/f20230627/day0627/src/views/Demo7.vue

      <template><div :class="sty['demo-box']"><h2 :class="sty.title">哈哈</h2><el-button type="danger" :class="sty.button">呵呵</el-button></div>
      </template><script>
      import sty from "./demo.module.css";
      console.log(`sty-->`, sty);
      export default {data() {return {sty};},
      };
      </script>
      
    • fang/f20230627/day0627/src/views/demo.module.css

      .demo-box{background: skyblue;
      }.title{font-size: 20px;
      }.button{border-radius: 0;
      }
      

进阶参考

  1. vue-全局配置
  2. 取消生产环境下的Vue提示信息。
  3. vuejs-devtools 的配置说明
  4. vuejs-devtools插件
  5. Vue-use
  6. 富文本编辑器

http://www.ppmy.cn/news/644529.html

相关文章

Adobe系列错误代码解决方案汇总

Adobe系列错误代码解决方案汇总 近来很多小伙伴在安装Adobe系列软件的时候遇到了这样那样的错误&#xff0c;在后台给我留言&#xff0c;在一个一个解决问题的时候我就萌生了把所有问题汇总起来的想法。 因为毕竟一个个回复不如把问题摊开来呈现给大家更为直接&#xff0c;当…

adobe黑体std能商用_adobe黑体std字体下载

Adobe 黑体 Std R字体又称之为adobeheitistd regular字体,是adobe官方专为设计师而开发的一款实用字体,该字体线条粗细适中,字形大方得体,整体效果非常出色,且字库完整,能够适用于产品包装、广告宣传、出版印刷、商标用字、商业网站、影视影像视频、书籍出版印刷等设计领…

mac显示无法连接adobe服务器,Mac安装Adobe软件,如遇Error提示解决方法

Mac10.15.3 安装Adobe Photoshop 2020的时候一直提示Error错误 The installation cannot continue as the installer file may be damaged. Download the installer file again. 看到这种问题&#xff0c;一般第一想法就是安装包损坏了&#xff0c;本能的会再下载一遍甚至多遍&…

adobe黑体std能商用_adobe字体版权?

刚刚查到了adobe的官网信息&#xff0c;有如下内容Font licensing center | Adobe Type​www.adobe.com 再直接贴上一段翻译过来的内容&#xff1a; 问&#xff1a;我可以将Adobe字体用于商业项目和客户端工作吗&#xff1f; 答&#xff1a;是的&#xff0c;Adobe的标准 字体许…

Adobe Acrobat Reader离线安装包下载

Adobe Acrobat Reader官网默认下载在线安装包&#xff0c;离线包下载地址可到https://get.adobe.com/cn/reader/enterprise/。

Adobe官网正版Ps+LrC软件免费拿,最高还可抽取99.99元牛年红包!

2021.2.8—2021.2.18成功报名Adobe国际认证职业技能认证&#xff0c;除可获得对应认证专家证书。还可免费享受Adobe国际认证牛年福利五重好礼 报名立减199元&#xff08;前20名免费领取Adobe定制马克杯&#xff09; Photoshop CCLightroom Classice正版软件订阅&#xff0c;免…

Adobe国际认证中国官网认证科目介绍

Adobe国际认证是Adobe公司推出的权威国际认证&#xff0c;在128个国家和地区均有发行&#xff0c;19种考试语言&#xff0c;是面向全球Adobe软件的学习和使用者&#xff0c;提供的一套全面科学&#xff0c;严谨高效的考核体系&#xff0c;通过实际应用操作的测评体系&#xff0…

PS学习_1-软件下载与破解

一.adobe软件下载 1.先上adobe官网http://www.adobe.com/cn/下载adobe Creative Cloud免费试用版本 这是一个adobe公司的产品客户端&#xff08;就像暴雪客户端一样&#xff0c;能下载公司的产品&#xff09;&#xff0c;下载后应该是这样&#xff1a; 没有Adobe Id就注册一个…