环境

  • 安装cli工具

    1
    npm install -g @vue/cli rimraf yrm

项目搭建

新建项目

  • 使用 vue 命令创建项目

    1
    2
    3
    vue create project_name
    cd project_name
    npm install

vscode配置

  • eslint: 定义规范

  • prettier: 根据eslint的规则格式化代码

  • babel: javaScript的编译器

  • vscode的设置

    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
    {
    // vscode默认启用了根据文件类型自动设置tabsize的选项
    "editor.detectIndentation": false,
    // 重新设定tabsize
    "editor.tabSize": 2,
    // #每次保存的时候自动格式化
    "editor.formatOnSave": true,
    // #每次保存的时候将代码按eslint格式进行修复
    "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
    },
    // 添加 vue 支持
    "eslint.validate": [
    "javascript",
    "javascriptreact",
    {
    "language": "vue",
    "autoFix": true
    }
    ],
    // #让prettier使用eslint的代码格式进行校验
    "prettier.eslintIntegration": true,
    // #去掉代码结尾的分号
    "prettier.semi": false,
    // #使用单引号替代双引号
    "prettier.singleQuote": true,
    // #让函数(名)和后面的括号之间加个空格
    "javascript.format.insertSpaceBeforeFunctionParenthesis": true,
    // #这个按用户自身习惯选择
    "vetur.format.defaultFormatter.html": "js-beautify-html",
    // #让vue中的js按编辑器自带的ts格式进行格式化
    "vetur.format.defaultFormatter.js": "vscode-typescript",
    "vetur.format.defaultFormatterOptions": {
    "js-beautify-html": {
    "wrap_attributes": "force-aligned"
    // #vue组件中html代码格式化样式
    }
    },
    "workbench.colorTheme": "Ayu Mirage",
    "workbench.iconTheme": "vscode-icons",
    "[jsonc]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "editor.quickSuggestions": {
    "strings": true
    },
    "vetur.ignoreProjectWarning": true,
    "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
    },
    "files.autoSave": "afterDelay"
    }
  • 插件

  • vue代码片段

    • Ctrl + Shift + P ,搜索 snippets, 找到vue.json
    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    {
    // Place your snippets for vue here. Each snippet is defined under a snippet name and has a prefix, body and
    // description. The prefix is what is used to trigger the snippet and the body will be expanded and inserted. Possible variables are:
    // $1, $2 for tab stops, $0 for the final cursor position, and ${1:label}, ${2:another} for placeholders. Placeholders with the
    // same ids are connected.
    // Example:
    // "Print to console": {
    // "prefix": "log",
    // "body": [
    // "console.log('$1');",
    // "$2"
    // ],
    // "description": "Log output to console"
    // }
    "Print to console": {
    "prefix": "vue",
    "body": [
    "<!-- $1 -->",
    "<template>",
    "<div class='$2'>$5</div>",
    "</template>",
    "",
    "<script>",
    "",
    "// 导入的其他文件 例如:import moduleName from 'modulePath';",
    "",
    "export default {",
    "",
    "//import所引入的组件注册",
    "components: {",
    "",
    "},",
    "",
    "data() {",
    " return {",
    "",
    " };",
    "},",
    "",
    "//监听属性",
    "computed: {",
    "",
    "},",
    "",
    "//监控data中的数据变化",
    "watch: {",
    "",
    "},",
    "",
    "//方法集合",
    "methods: {",
    "",
    "},",
    "",
    "//生命周期 - 组件实例刚被创建",
    "beforeCreate() { ",
    "",
    "},",
    "//创建完成 访问当前this实例",
    "created() {",
    "",
    "},",
    "//挂载之前",
    "beforeMount() { ",
    "",
    "},",
    "//挂载完成 访问DOM元素",
    "mounted() {",
    "",
    "},",
    "//更新之前",
    "beforeUpdate() { ",
    "",
    "},",
    "//更新之后",
    "updated() { ",
    "",
    "},",
    "//for keep-alive 缓存功能,组件被激活时调用",
    "activated() {",
    "",
    "},",
    "//for keep-alive 组件被移除时调用",
    "deactivated() {",
    "",
    "},",
    "//组件销毁之前调用",
    "beforeDestroy() {",
    "",
    "},",
    "//组件销毁之后调用",
    "destroyed() {",
    "",
    "},",
    "}",
    "</script>",
    "<style lang='scss' scoped>",
    "//@import url($3); 引入公共css类",
    "$4",
    "</style>"
    ],
    "description": "Log output to console"
    }
    }

vue-cli

  • https://cli.vuejs.org/zh/guide/

  • Vue CLI 是一个基于 Vue.js 进行快速开发的完整系统,提供:

    • 通过 @vue/cli 实现的交互式的项目脚手架。
    • 通过 @vue/cli + @vue/cli-service-global 实现的零配置原型开发。
    • 一个运行时依赖 (@vue/cli-service),该依赖:
      • 可升级;
      • 基于 webpack 构建,并带有合理的默认配置;
      • 可以通过项目内的配置文件进行配置;
      • 可以通过插件进行扩展。
    • 一个丰富的官方插件集合,集成了前端生态中最好的工具。
    • 一套完全图形化的创建和管理 Vue.js 项目的用户界面。

配置

  • 如何配置Vue-CL创建项目的 webpack配置
    • 认情况下通过Wue-CLI创建的项目己经自动给我们配置好了 webpack
  • 但是有时候默认的配置可能不能满足我们的需求,例如我们想修改输出目录名称,想増加一些插件等,但是vue-CLI创建的项目里又没有 webpack配置文件,那么我们应该如何修改或增加 webpack配置呢?

vue.config.js

  • 常用选项如下

    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
    const path = require("path");
    function resolve(dir) {
    path.join(__dirname, dir);
    }

    module.exports = {
    //打包文件输出路径,即打包到哪里
    outputDir: "dist",
    // 静态资源地址
    assetsDir: "static",
    // eslint-loader 是否在保存的时候检查
    lintOnSave: false,
    // 生产环境是否生成 sourceMap 文件
    productionSourceMap: false,
    filenameHashing: true, //文件hash

    // chainWebpack 这个库提供了一个 webpack 原始配置的上层抽象,使其可以定义具名的 loader 规则
    // 和具名插件,可以通过其提供的一些方法链式调用,在cli-service中就使用了这个插件
    chainWebpack: () => {},

    /*
    configureWebpack是调整webpack配置最简单的一种方式,可以新增也可以覆盖cli中的配置。
    可以是一个对象:被 webpack-merge 合并到webpack 的设置中去
    也可以是一个函数:如果你需要基于环境有条件地配置行为,就可以进行一些逻辑处理,可以直接修改或
    新增配置,(该函数会在环境变量被设置之后懒执行)。该方法的第一个参数会收到已经解析好的配置。
    在函数内,你可以直接修改配置,或者返回一个将会被合并的对象。
    */
    configureWebpack: {},
    };

alias

  • 使用chainWebpack

    1
    2
    3
    4
    5
    chainWebpack: (config) => {
    config.resolve.alias
    // key,value自行定义,比如.set('@assets', resolve('src/assets'))
    .set(key, value);
    };
  • configureWebpack方法

    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
    // 对象形式
    configureWebpack: {
    resolve: {
    alias: {
    '@assets': resolve('src/assets')
    }
    }
    };

    // 函数形式
    configureWebpack: config => {
    if (isProduction) {
    ...
    } else {
    ...
    }
    // 方式一:直接修改配置
    config.resolve.alias['@asset'] = resolve('src/assets')
    //方式二: 返回一个将要合并的对象
    return {
    resolve: {
    alias: {
    '@asset':resolve('src/assets')
    }
    }
    }
    }

全部配置

  • 示例

    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
    const path = require("path");
    function resolve(dir) {
    path.join(__dirname, dir);
    }

    module.exports = {
    publicPath: "./", // 部署应用包时的基本 URL Default: '/'
    outputDir: "dist",
    assetsDir: "static", // 放置生成的静态资源 (js、css、img、fonts) 的 (相对于 outputDir 的) 目录。
    indexPath: "index.html", // 指定生成的 index.html 的输出路径 (相对于 outputDir)。也可以是一个绝对路径。 Default: 'index.html'
    configureWebpack: (config) => {
    if (process.env.NODE_ENV === "production") {
    // 为生产环境修改配置...
    config.mode = "production";
    } else {
    // 为生产环境修改配置...
    config.mode = "development";
    }
    // 开发生产共同配置别名
    Object.assign(config.resolve, {
    alias: {
    "@": path.resolve(__dirname, "./src"),
    assets: path.resolve(__dirname, "./src/assets"),
    common: path.resolve(__dirname, "./src/common"),
    components: path.resolve(__dirname, "./src/components"),
    network: path.resolve(__dirname, "./src/network"),
    configs: path.resolve(__dirname, "./src/configs"),
    views: path.resolve(__dirname, "./src/views"),
    plugins: path.resolve(__dirname, "./src/plugins"),
    },
    });
    },
    };

全局样式

  • 在 main.js 中引入

    1
    import "@/styles/index.scss"; // global css
  • 在 src/styles/index.scc中编写

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    @import "./element-ui.scss";

    body {
    margin: 0;
    padding: 0;
    height: 100%;
    -moz-osx-font-smoothing: grayscale;
    -webkit-font-smoothing: antialiased;
    text-rendering: optimizeLegibility;
    font-family:
    Helvetica Neue,
    Helvetica,
    PingFang SC,
    Hiragino Sans GB,
    Microsoft YaHei,
    Arial,
    sans-serif;
    }

    label {
    font-weight: 700;
    }

    html {
    height: 100%;
    box-sizing: border-box;
    }

    #app {
    height: 100%;
    }

    *,
    *:before,
    *:after {
    box-sizing: inherit;
    }

    a:focus,
    a:active {
    outline: none;
    }

    a,
    a:focus,
    a:hover {
    cursor: pointer;
    color: inherit;
    text-decoration: none;
    }

    div:focus {
    outline: none;
    }

    .clearfix {
    &:after {
    visibility: hidden;
    display: block;
    font-size: 0;
    content: " ";
    clear: both;
    height: 0;
    }
    }

    // main-container global css
    .app-container {
    padding: 20px;
    }

引入element UI

按需引入

  • 安装babel-plugin-component

    1
    npm install babel-plugin-component -D
  • 在 babel.config.js 文件中添加下面配置

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    module.exports = {
    presets: ["@vue/cli-plugin-babel/preset"],
    plugins: [
    [
    "component",
    {
    libraryName: "element-ui",
    styleLibraryName: "theme-chalk",
    },
    ],
    ],
    };
  • main.js 中引入element UI 组件

    1
    2
    3
    4
    5
    import Vue from "vue";
    import { Button, Select } from "element-ui";

    Vue.component(Button.name, Button);
    Vue.component(Select.name, Select);

完整列表

  • ElementUI 完整的组件列表如下

    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
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    import Vue from "vue";
    import {
    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,
    Badge,
    Card,
    Rate,
    Steps,
    Step,
    Carousel,
    CarouselItem,
    Collapse,
    CollapseItem,
    Cascader,
    ColorPicker,
    Transfer,
    Container,
    Header,
    Aside,
    Main,
    Footer,
    Loading,
    MessageBox,
    Message,
    Notification,
    } from "element-ui";

    Vue.use(Pagination);
    Vue.use(Dialog);
    Vue.use(Autocomplete);
    Vue.use(Dropdown);
    Vue.use(DropdownMenu);
    Vue.use(DropdownItem);
    Vue.use(Menu);
    Vue.use(Submenu);
    Vue.use(MenuItem);
    Vue.use(MenuItemGroup);
    Vue.use(Input);
    Vue.use(InputNumber);
    Vue.use(Radio);
    Vue.use(RadioGroup);
    Vue.use(RadioButton);
    Vue.use(Checkbox);
    Vue.use(CheckboxButton);
    Vue.use(CheckboxGroup);
    Vue.use(Switch);
    Vue.use(Select);
    Vue.use(Option);
    Vue.use(OptionGroup);
    Vue.use(Button);
    Vue.use(ButtonGroup);
    Vue.use(Table);
    Vue.use(TableColumn);
    Vue.use(DatePicker);
    Vue.use(TimeSelect);
    Vue.use(TimePicker);
    Vue.use(Popover);
    Vue.use(Tooltip);
    Vue.use(Breadcrumb);
    Vue.use(BreadcrumbItem);
    Vue.use(Form);
    Vue.use(FormItem);
    Vue.use(Tabs);
    Vue.use(TabPane);
    Vue.use(Tag);
    Vue.use(Tree);
    Vue.use(Alert);
    Vue.use(Slider);
    Vue.use(Icon);
    Vue.use(Row);
    Vue.use(Col);
    Vue.use(Upload);
    Vue.use(Progress);
    Vue.use(Badge);
    Vue.use(Card);
    Vue.use(Rate);
    Vue.use(Steps);
    Vue.use(Step);
    Vue.use(Carousel);
    Vue.use(CarouselItem);
    Vue.use(Collapse);
    Vue.use(CollapseItem);
    Vue.use(Cascader);
    Vue.use(ColorPicker);
    Vue.use(Container);
    Vue.use(Header);
    Vue.use(Aside);
    Vue.use(Main);
    Vue.use(Footer);

    Vue.use(Loading.directive);

    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;

引入sass

  • 安装对应的 loader 即可, sass-loader最新版本需要 webpack5,这里指定版本安装

    1
    2
    npm install sass-loader@^7.2.0 -D
    npm install sass@^1.35.2 -D
  • 使用

    1
    <style lang="sass" scoped></style>

引入路由

  • 安装依赖, 有二种方式

    1
    2
    3
    4
    # 方式一: vue命令安装
    vue add router
    # 方式二:npm 手动安装
    npm install vue-router --save
  • 新建 router/index.js 文件

    1
    2
    3
    4
    5
    import Vue from "vue";
    import VueRouter from "vue-router";

    // 使用VUE挂载vue-router
    Vue.use(VueRouter);
  • main.js导入

    1
    import router from './router'
  • 使用路由

    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
    export const constantRoutes = [
    {
    path: "/about",
    component: () => import("@/views/About"),
    hidden: true,
    },
    {
    path: "/home",
    component: () => import("@/views/Home"),
    },
    {
    path: "/login",
    component: () => import("@/views/login/index"),
    hidden: true,
    },
    ];

    const createRouter = () =>
    new VueRouter({
    // mode: 'history', // require service support
    scrollBehavior: () => ({ y: 0 }),
    routes: constantRoutes,
    });

    const router = createRouter();

    // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465
    export function resetRouter() {
    const newRouter = createRouter();
    router.matcher = newRouter.matcher; // reset router
    }
    export default router;

引入Axios

  • 文档

  • 安装

    1
    npm install vue-axios --save
  • 在main.js页面引用:

    1
    2
    3
    import Vue from "vue";
    import axios from "axios";
    Vue.prototype.$axios = axios; //全局注册,使用方法为:this.$axios
  • 使用示例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    <script>
    export default{
    data(){
    return{
    userId:666,
    token:'',
    }
    },
    created(){
    this.$axios({
    method:'post',
    url:'api',
    data:this.qs.stringify({ //这里是发送给后台的数据
    userId:this.userId,
    token:this.token,
    })
    }).then((response) =>{ //这里使用了ES6的语法
    console.log(response) //请求成功返回的数据
    }).catch((error) =>
    console.log(error) //请求失败返回的数据
    })
    }
    }
    </script>
  • 多个请求

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    function getUserAccount() {
    return axios.get("/user/12345");
    }

    function getUserPermissions() {
    return axios.get("/user/12345/permissions");
    }

    axios.all([getUserAccount(), getUserPermissions()]).then(
    axios.spread(function (acct, perms) {
    // Both requests are now complete
    }),
    );
  • 模版方法

    1
    2
    3
    4
    5
    6
    // axios.creat([config])
    var instance = axios.create({
    baseURL: "https://some-domain.com/api/",
    timeout: 1000,
    headers: { "X-Custom-Header": "foobar" },
    });

axios拦截器

  • 在请求前会出现一个动态的loading图,在响应后隐藏此loading图。

  • 在请求或响应被 thencatch 处理前拦截它们。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    // 添加请求拦截器
    axios.interceptors.request.use(
    function (config) {
    // 在发送请求之前做些什么
    return config;
    },
    function (error) {
    // 对请求错误做些什么
    return Promise.reject(error);
    },
    );

    // 添加响应拦截器
    axios.interceptors.response.use(
    function (response) {
    // 对响应数据做点什么
    return response;
    },
    function (error) {
    // 对响应错误做点什么
    return Promise.reject(error);
    },
    );