Vue(1706E)

目录

 

一、webpack相关

1、最简单的配置

2、最简单的vue环境

3、vue+devserver

4、html-webpack-plugin插件

5、使用babel-loader将js文件里的ES6语法转换为ES5语法

6、devtool

7、style-loader、css-loader

8、sass-loader

9、url-loader和file-loader

10、mini-css-extract-plugin(提取样式文件)

11、clean-webpack-plugin

12、配置src目录访问别名,自动解析确定的扩展

13、字体文件

二、vue相关

1、使用脚手架生成第一个项目

2、添加vue.config.js文件

3、render: h => h(App)的详细解释

4、组件的data为什么是一个函数

5、生命周期

6、组件

7、mock数据

8、上拉加载更多

9、鼠标滑过现实消息框

11、对话框组件

12、具名插槽(slot)

13、最简路由demo

14、路由基础

15、动态路由

16、嵌套路由

17、事件修饰符

18、输入框自动获取焦点

19、props

20、keep-alive

21、禁用eslint

22、计算属性缓存 vs 方法

23、watch监听属性

24、缩写

25、购物车

26、吸顶

17、盒模型

18、单双排和模糊搜索

19、展开折叠和loading效果

20、通讯录

项目github源码


 

一、webpack相关

1、最简单的配置

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: __dirname +  '/dist',
    filename: 'bundle.js'
  }
}

2、最简单的vue环境

const VueLoaderPlugin = require('vue-loader/lib/plugin.js') 

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

vue-loader配置参考链接:

https://vue-loader.vuejs.org/zh/guide/#%E6%89%8B%E5%8A%A8%E8%AE%BE%E7%BD%AE

3、vue+devserver

const VueLoaderPlugin = require('vue-loader/lib/plugin.js') 
const webpack = require('webpack')

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  devServer: {
    hot: true,
    inline: true,
    port: 8080,
    contentBase: __dirname + '/dist',
    open: true
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new webpack.HotModuleReplacementPlugin()
  ]
}

devServer.inline

在 dev-server 的两种不同模式之间切换。默认情况下,应用程序启用内联模式(inline mode)。这意味着一段处理实时重载的脚本被插入到你的包(bundle)中,并且构建消息将会出现在浏览器控制台。

也可以使用 iframe 模式,它在通知栏下面使用 <iframe> 标签,包含了关于构建的消息。切换到 iframe 模式

inline:true 代码改变时刷新浏览器:

inline:false 代码改变时也会刷新浏览器:

hot:true

启用 webpack 的模块热替换特性

代码更新时会重新加载改变的部分,而不是刷新整个页面,性能比较高

Note that webpack.HotModuleReplacementPlugin is required to fully enable HMR. If webpack or webpack-dev-server are launched with the --hot option, this plugin will be added automatically, so you may not need to add this to your webpack.config.js. See the HMR concepts page for more information.

hot:true,热更新插件会自动添加上,不需要手动添加

const VueLoaderPlugin = require('vue-loader/lib/plugin.js') 

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  devServer: {
    hot: true,
    inline: true,
    port: 8080,
    contentBase: __dirname + '/dist',
    open: true
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin()
  ]
}

4、html-webpack-plugin插件

参考位置:webpack官网-》中文文档-》插件-》HtmlWebpackPlugin

参考链接:

https://www.webpackjs.com/plugins/html-webpack-plugin/

当出口文件包含hash值是,可以动态的引用编译出来的包含hash值的文件

const VueLoaderPlugin = require('vue-loader/lib/plugin.js') 
const htmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'main-[hash].js'
  },
  devServer: {
    hot: true,
    inline: true,
    port: 8080,
    contentBase: __dirname + '/dist',
    open: true
  },
  module: {
    rules: [
      {
        test: /.vue$/,
        loader: 'vue-loader'
      }
    ]
  },
  plugins: [
    new VueLoaderPlugin(),
    new htmlWebpackPlugin({
      template: __dirname + '/dist/index.html'
    })
  ]
}

5、使用babel-loader将js文件里的ES6语法转换为ES5语法

首页安装三的npm包:

yarn add babel-loader @babel/core @babel/preset-env

Babel其实是几个模块化的包,其核心功能位于称为@babel-core的npm包中,webpack可以把其不同的包整合在一起使用,对于每一个你需要的功能或拓展,你都需要安装单独的包,用得最多的是解析ES6的@babel/preset-env包。

const htmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    })
  ]
}

箭头函数编译后变成普通函数:

const test = () => {
  console.log('hello babel!1')
}

test()

6、devtool

devtool用作调试,在找错误方面给了我们很大的帮助,然而官网的devtool有十几种配置,最常用的是source-map。

source-map这种配置会生成一个带有.map文件,这个map文件会和原始文件做一个映射,调试的时候,就是通国这个.map文件去定位原来的代码位置的。

 参考链接:

https://www.jianshu.com/p/62dc120d96d0

编译后会生成一个后缀为.map的文件:

调试的时候可以看到打包前的源代码,如果没有设置devtool则看不到:

7、style-loader、css-loader

装包:

yarn add style-loader css-loader

建议将 style-loadercss-loader 结合使用,syle-loader和css-loader作用是不同的。

css-loader:加载.css文件

style-loader: 使用<style>标签将css-loader内部样式注入到HTML页面。这是通过js动态注入的。

const htmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }, {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    })
  ]
}

main.js使用import引入css文件

import './index.css'

const test = () => {
  console.log('hello babel!1')
}

test()

index.css:

body{background: #dddddd;}

打包后的js文件里包含注入到页面的代码:

注入后的效果,body背景色变成灰色了:

8、sass-loader

装包:

yarn add node-sass sass-loader
const htmlWebpackPlugin = require("html-webpack-plugin")

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      }, {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }
        ]
      }, {
        test: /.scss$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }, {
            loader: 'sass-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    })
  ]
}

index.scss:

body{
  background: #dddddd;
  #app{
    height: 100px;
    background: #f66f0c;
  }
}

注入页面后的效果:

9、url-loader和file-loader

装包:

yarn add url-loader
  module: {
    rules: [
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader:'url-loader',
            options: {
              limit: 50000,
              name: '[path][name]-[hash].[ext]'
            }
          }
        ]
      }
    ]
  },

limit的单位是B,50000代表50kb,小于50kb的图片将转换成base64的格式,大于50kb的图片则通过url访问,转换成base64的好处是减少图片的请求,缺点的图片会失帧。可以通过img标签的src属性引用图片,也可以通过background-img:url()引用图片。

Index.vue:

<template>
  <div>
    <div class="m-message">{{message}}</div>
    <img :src="clothes_44kb">
    <img :src="clothes_79kb">
    <div class="m-clothes-44kb"></div>
    <div class="m-clothes-79kb"></div>
    <div class="m-count">{{count}}</div>
    <div>
      <button @click="handleSub">减</button>
      <button @click="handleAdd">加</button>
    </div>
  </div>
</template>

<script>
import clothes_44kb from '../static/images/clothes_44kb.png'
import clothes_79kb from '../static/images/clothes_79kb.png'

export default {
  data() {
    return {
      message: 'hello vue!1',
      clothes_44kb: clothes_44kb,
      clothes_79kb: clothes_79kb,
      count: 0
    }
  },
  methods: {
    handleAdd() {
      this.count = this.count + 1
    },
    handleSub() {
      this.count = this.count - 1
    }
  }
}
</script>

<style>
.m-count{font-size: 36px;color: #ff0000;}
</style>

index.scss:

.m-clothes-44kb{display: inline-block; 159px;height: 178px;background-image: url(./static/images/clothes_44kb.png);background-size: 100% 100%;}
.m-clothes-79kb{display: inline-block; 159px;height: 178px;background-image: url(./static/images/clothes_79kb.png);background-size: 100% 100%;}

file-loader装包:

yarn add file-loader
  module: {
    rules: [
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader:'file-loader',
            options: {
              name: '[path][name]-[hash].[ext]'
            }
          }
        ]
      }
    ]
  },

file-loader没有limit字段,所以无论图片大小是多少,都是通过url访问。url-loader可以代替file-loader。

10、mini-css-extract-plugin(提取样式文件)

装包:

yarn add mini-css-extract-plugin
const htmlWebpackPlugin = require("html-webpack-plugin")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'main-[hash].js'
  },
  devServer: {
    contentBase: __dirname + '/dist',
    port: 8080,
    inline: true,
    open: true,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
       {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: MiniCssExtractPlugin.loader
          }, {
            loader: 'css-loader'
          }
        ]
      }, 
      {
        test: /.scss$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }, {
            loader: 'sass-loader'
          }
        ]
      }, 
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader:'url-loader',
            options: {
              limit: 50000,
              name: '[path][name]-[hash].[ext]'
            }
          }
        ]
      }, 
      {
        test: /.vue$/,
        use: [
          {
            loader: 'vue-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
  ]
}

参考链接:

https://www.npmjs.com/package/mini-css-extract-plugin

11、clean-webpack-plugin

打包后的文件添加hash值是为了合理的使用浏览器的缓存,但是每次打包后由于hash值都会变,所以会产生很多残余的文件,可以使用clean-webpack-plugin插件清除残余文件

const htmlWebpackPlugin = require("html-webpack-plugin")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require("clean-webpack-plugin")

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'main-[hash].js'
  },
  devServer: {
    contentBase: __dirname + '/dist',
    port: 8080,
    inline: true,
    open: true,
    hot: true,
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
       {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: MiniCssExtractPlugin.loader
          }, {
            loader: 'css-loader'
          }
        ]
      }, 
      {
        test: /.scss$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }, {
            loader: 'sass-loader'
          }
        ]
      }, 
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader:'url-loader',
            options: {
              limit: 50000,
              name: '[path][name]-[hash].[ext]'
            }
          }
        ]
      }, 
      {
        test: /.vue$/,
        use: [
          {
            loader: 'vue-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
    new CleanWebpackPlugin()
  ]
}

参考链接:

https://www.npmjs.com/package/clean-webpack-plugin

12、配置src目录访问别名,自动解析确定的扩展

关键代码:

  resolve: {
    extensions: ['.js', '.vue'],
    alias: {
      '@': __dirname + '/src'
    }
  },

全部代码:

const htmlWebpackPlugin = require("html-webpack-plugin")
const VueLoaderPlugin = require("vue-loader/lib/plugin")
const MiniCssExtractPlugin = require('mini-css-extract-plugin')
const { CleanWebpackPlugin } = require("clean-webpack-plugin")

module.exports = {
  mode: 'development',
  devtool: 'source-map',
  entry: './src/main.js',
  output: {
    path: __dirname + '/dist',
    filename: 'main-[hash].js'
  },
  devServer: {
    contentBase: __dirname + '/dist',
    port: 8080,
    inline: true,
    open: true,
    hot: true,
  },
  resolve: {
    extensions: ['.js', '.vue'],
    alias: {
      '@': __dirname + '/src'
    }
  },
  module: {
    rules: [
      {
        test: /.js$/,
        exclude: /node_modules/,
        use: {
          loader: 'babel-loader',
          options: {
            presets: ['@babel/preset-env']
          }
        }
      },
      {
        test: /.css$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: MiniCssExtractPlugin.loader
          }, {
            loader: 'css-loader'
          }
        ]
      },
      {
        test: /.scss$/,
        use: [
          {
            loader: 'style-loader'
          }, {
            loader: 'css-loader'
          }, {
            loader: 'sass-loader'
          }
        ]
      },
      {
        test: /.(png|jpg|gif)$/,
        use: [
          {
            loader: 'url-loader',
            options: {
              limit: 50000,
              name: '[path][name]-[hash].[ext]'
            }
          }
        ]
      },
      {
        test: /.vue$/,
        use: [
          {
            loader: 'vue-loader'
          }
        ]
      }
    ]
  },
  plugins: [
    new htmlWebpackPlugin({
      template: __dirname + '/src/index.html'
    }),
    new VueLoaderPlugin(),
    new MiniCssExtractPlugin({
      filename: '[name].css'
    }),
    new CleanWebpackPlugin()
  ]
}

引用的时候用法如下(@直接src目录,可以省略.vue后缀名):

import Index from '@/pages/Index'

13、字体文件

使用file-lader:

      {
        test: /.(eot|svg|ttf|woff|woff2)$/,
        use: [
          {
            loader: 'file-loader'
          }
        ]
      },

使用url-loader:

      {
        test: /.(eot|svg|ttf|woff|woff2)$/,
        use: [
          {
            loader: 'url-loader'
          }
        ]
      },

页面里使用字体:

    <div>
      <span class="icon iconfont icon-naozhong m-icon"></span>
      <span class="icon iconfont icon-weibiaoti- m-icon"></span>
      <span class="icon iconfont icon-shoudaobaodan m-icon"></span>
    </div>

自定义样式:

.m-icon{font-size: 36px!important;color: #f66f0c;}

效果:

如何获取字体文件?参考链接:

https://www.iconfont.cn/

二、vue相关

1、使用脚手架生成第一个项目

安装脚手架:

yarn global add @vue/cli

在终端创建项目:

vue create m-app

选择手动配置:

可以按键盘上的方向键选择你需要的组件,比如Router和Vuex,按空格选择或取消选择:

其余各项一路回车。

项目生成完成后执行下面两条命令启动项目:

cd m-app

yarn serve

脚手架生成的项目的目录结构:

2、添加vue.config.js文件

参考链接:

https://cli.vuejs.org/zh/guide/webpack.html#%E7%AE%80%E5%8D%95%E7%9A%84%E9%85%8D%E7%BD%AE%E6%96%B9%E5%BC%8F

vue.config.js:

const bodyParser = require('body-parser')
const { list } = require('./data.js')

module.exports = {
  configureWebpack: {
    devServer: {
      open: true,
      before(app) {
        app.use(bodyParser.json())
  
        app.get('/api/list', (req, res) => {
          res.send({
            code: 200,
            data: list,
            message: '列表'
          })
        })
  
        app.post('/api/login', (req, res) => {
          let { username, password } = req.body
          if (username === 'admin' && password === '123456') {
            res.send({
              code: 200,
              data: {
                username
              },
              message: '登录成功'
            })
          } else {
            res.send({
              code: 400,
              data: {
                username
              },
              message: '登录失败'
            })
          }
        })
      }
    }
  }
}

data.js:

const list = [{
  id: 0,
  name: '张三'
}, {
  id: 1,
  name: '李四'
}]

module.exports = {
  list
}

get请求可以直接的浏览器里测试:

post请求可以用postman测试:

也是可以写axios请求测试post接口:

      axios({
        url: '/api/login',
        data: {
          username: 'admin',
          password: '123456'
        },
        method: 'post'
      }).then(res => {
        console.log(res)
      })

3、render: h => h(App)的详细解释

脚手架生成的代码:

new Vue({
  render: h => h(App),
}).$mount('#app')

换成好理解的写法:

new Vue({
  render: function (createElement) {
    return createElement(App)
  }
}).$mount("#app")

为啥用h代替createElement?尤雨溪是这么说的:

It comes from the term "hyperscript", which is commonly used in many virtual-dom implementations. "Hyperscript" itself stands for "script that generates HTML structures" because HTML is the acronym for "hyper-text markup language".

它来自单词 hyperscript,这个单词通常用在 virtual-dom 的实现中。Hyperscript 本身是指
生成HTML 结构的 script 脚本,因为 HTML 是 hyper-text markup language 的缩写(超文本标记语言)

参考链接:

https://blog.csdn.net/helenwei2017/article/details/80743122

4、组件的data为什么是一个函数

<script>

export default {
  data() {
    return {
      message: "hello vue!",
    }
  }
}
</script>

组件的data数据为什么必须要以函数返回的形式,为什么不是简单的对象形式呢?

组件是可复用的vue实例,一个组件被创建好之后,就可能被用在各个地方,而组件不管被复用了多少次,组件中的data数据都应该是相互隔离,互不影响的,基于这一理念,组件每复用一次,data数据就应该被复制一次,之后,当某一处复用的地方组件内data数据被改变时,其他复用地方组件的data数据不受影响。

参考链接:

https://cn.vuejs.org/v2/guide/components.html#data-%E5%BF%85%E9%A1%BB%E6%98%AF%E4%B8%80%E4%B8%AA%E5%87%BD%E6%95%B0

https://www.cnblogs.com/wangjiachen666/p/9876266.html

5、生命周期

什么是生命周期:从Vue实例创建、运行、到销毁期间,总是伴随着各种各样的事件,这些事件,统称为生命周期

生命周期钩子:就是生命周期事件的别名

自然界的万事万物都会有一个生命周期,比如人,生老病死,比如石头,沉淀风化,最后成为泥土。

所以vue的组件也是有生命周期的,三个阶段,八个钩子


创建期间的生命周期函数:
      beforeCreate:实例刚在内存中被创建出来,此时,还没有初始化好 data 和 methods 属性
      created:实例已经在内存中创建OK,此时 data 和 methods 已经创建OK,此时还没有开始 编译模板
      beforeMount:此时已经完成了模板的编译,但是还没有挂载到页面中
      mounted:此时,已经将编译好的模板,挂载到了页面指定的容器中显示
 运行期间的生命周期函数:
      beforeUpdate:状态更新之前执行此函数, 此时 data 中的状态值是最新的,但是界面上显示的 数据还是旧的,因为此时还   没有开始重新渲染DOM节点
      updated:实例更新完毕之后调用此函数,此时 data 中的状态值 和 界面上显示的数据,都已经完成了更新,界面已经被重新渲染好了!
   销毁期间的生命周期函数:
      beforeDestroy:实例销毁之前调用。在这一步,实例仍然完全可用。
      destroyed:Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。

<!DOCTYPE html>
<html lang="en">
 
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script type="text/javascript" src="../lib/vue-2.4.0.js" ></script>
  <link rel="stylesheet" href="../lib/bootstrap-3.3.7.css"></link>
</head>
 
<body>
  <div id="app">
    <input type="button" value="修改msg" @click="msg='No'">
    <h3 id="h3">{{ msg }}</h3>
  </div>
 
  <script>
    // 创建 Vue 实例,得到 ViewModel
    var vm = new Vue({
      el: '#app',
      data: {
        msg: 'ok'
      },
      methods: {
        show() {
          console.log('执行了show方法')
        }
      },
      beforeCreate() { // 这是我们遇到的第一个生命周期函数,表示实例完全被创建出来之前,会执行它
        // console.log(this.msg)
        // this.show()
        // 注意: 在 beforeCreate 生命周期函数执行的时候,data 和 methods 中的 数据都还没有没初始化
      },
      created() { // 这是遇到的第二个生命周期函数
        // console.log(this.msg)
        // this.show()
        //  在 created 中,data 和 methods 都已经被初始化好了!
        // 如果要调用 methods 中的方法,或者操作 data 中的数据,最早,只能在 created 中操作
      },
      beforeMount() { // 这是遇到的第3个生命周期函数,表示 模板已经在内存中编辑完成了,但是尚未把 模板渲染到 页面中
        // console.log(document.getElementById('h3').innerText)
        // 在 beforeMount 执行的时候,页面中的元素,还没有被真正替换过来,只是之前写的一些模板字符串
      },
      mounted() { // 这是遇到的第4个生命周期函数,表示,内存中的模板,已经真实的挂载到了页面中,用户已经可以看到渲染好的页面了
        // console.log(document.getElementById('h3').innerText)
        // 注意: mounted 是 实例创建期间的最后一个生命周期函数,当执行完 mounted 就表示,实例已经被完全创建好了,此时,如果没有其它操作的话,这个实例,就静静的 躺在我们的内存中,一动不动
      },
 
 
      // 接下来的是运行中的两个事件
      beforeUpdate() { // 这时候,表示 我们的界面还没有被更新【数据被更新了吗?  数据肯定被更新了】
        /* console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 数据是:' + this.msg) */
        // 得出结论: 当执行 beforeUpdate 的时候,页面中的显示的数据,还是旧的,此时 data 数据是最新的,页面尚未和 最新的数据保持同步
      },
      updated() {
        console.log('界面上元素的内容:' + document.getElementById('h3').innerText)
        console.log('data 中的 msg 数据是:' + this.msg)
        // updated 事件执行的时候,页面和 data 数据已经保持同步了,都是最新的
      }
    });
  </script>
</body>
 
</html>

6、组件

使用Vue编写Header、Footer、Content三个组件,点击底部可以实现tab切换:

当使用 kebab-case (短横线分隔命名) 定义一个组件时,你也必须在引用这个自定义元素时使用 kebab-case,例如 <my-component-name>

当使用 PascalCase (首字母大写命名) 定义一个组件时,你在引用这个自定义元素时两种命名法都可以使用。也就是说 <my-component-name><MyComponentName> 都是可接受的。注意,尽管如此,直接在 DOM (即非字符串的模板) 中使用时只有 kebab-case 是有效的。

在模块系统中局部注册:

import ComponentA from './ComponentA'
import ComponentC from './ComponentC'

export default {
  components: {
    ComponentA,
    ComponentC
  },
  // ...
}

Prop 的大小写:

HTML 中的特性名是大小写不敏感的,所以浏览器会把所有大写字符解释为小写字符。这意味着当你使用 DOM 中的模板时,camelCase (驼峰命名法) 的 prop 名需要使用其等价的 kebab-case (短横线分隔命名) 命名:

Vue.component('blog-post', {
  // 在 JavaScript 中是 camelCase 的
  props: ['postTitle'],
  template: '<h3>{{ postTitle }}</h3>'
})
<!-- 在 HTML 中是 kebab-case 的 -->
<blog-post post-title="hello!"></blog-post>

 重申一次,如果你使用字符串模板,那么这个限制就不存在了。

Prop 类型:

props: ['title', 'likes', 'isPublished', 'commentIds', 'author']
props: {
  title: String,
  likes: Number,
  isPublished: Boolean,
  commentIds: Array,
  author: Object,
  callback: Function,
  contactsPromise: Promise // or any other constructor
}

传入一个数字:

<!-- 即便 `42` 是静态的,我们仍然需要 `v-bind` 来告诉 Vue -->
<!-- 这是一个 JavaScript 表达式而不是一个字符串。-->
<blog-post v-bind:likes="42"></blog-post>

所有的 prop 都使得其父子 prop 之间形成了一个单向下行绑定:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

额外的,每次父级组件发生更新时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器的控制台中发出警告。

对于绝大多数特性来说,从外部提供给组件的值会替换掉组件内部设置好的值。所以如果传入 type="text" 就会替换掉 type="date" 并把它破坏!庆幸的是,classstyle 特性会稍微智能一些,即两边的值会被合并起来

始终使用 kebab-case 的事件名

7、mock数据

装包:

yarn add mockjs

造数据:

const Mock = require('mockjs')

const mockDataList = Mock.mock({
	'list|20': [{
		'id|+1': 1,
		'name': '@cname',
		'title': '@ctitle',
		'image': '@image(300x300)',
		'address': '@county(true)'
	}]
})

console.log(mockDataList)

参考链接:

https://www.jianshu.com/p/d8f8afd5f238

mock的数据:

8、上拉加载更多

装包:

yarn add vue-scroller

参考链接:

https://github.com/wangdahoo/vue-scroller

9、鼠标滑过现实消息框

多分几个组件:

11、对话框组件

<template>
  <div class="m-dialog-wrap" :class="{active:visible}" >
    <div class="m-dialog">
      <div class="m-dialog-title">{{title}}</div>
      <span class="m-dialog-close" @click="handleCancel">X</span>
      <slot></slot>
      <div class="m-dialog-footer">
        <span v-for="(item, index) in btns" :key="index" @click="item.handler" class="m-dialog-btn">{{item.text}}</span>
      </div>
    </div>
  </div>
</template>

<script>
export default {
  props: ["visible", 'title', 'onCancel', 'btns'],
  methods: {
    handleCancel() {
      this.$emit('onCancel')
    }
  }
}
</script>

<style>

</style>

12、具名插槽(slot)

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Vue</title>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>

<body>
  <div id="app">
    <my-header>
      <template v-slot:btn>
        <button>按钮</button>
      </template>
      <template #my-input>
        <input value="xu"></input>
      </template>
    </my-header>
  </div>

  <script>
    Vue.component('my-header', {
      template: `
        <div>
          <div>hello slot!</div>
          <slot name="btn"></slot>
          <slot name="my-input"></slot>
        </div>
      `
    })

    var watchExampleVM = new Vue({
      el: '#app'
    })
  </script>
</body>

</html>

13、最简路由demo

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Vue路由</title>
  <style>
    .router-link-active {
      color: red;
    }
  </style>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
  <script src="https://cdn.bootcss.com/vue-router/3.1.3/vue-router.min.js"></script>
</head>

<body>
  <div id="app">
    <router-link to="/login">登录</router-link>
    <router-link to="/index">首页</router-link>
    <router-view></router-view>
  </div>
  <script>
    var router = new VueRouter({
      routes: [
        {
          path: '/login',
          component: { template: '<div>登录</div>' }
        }, {
          path: '/index',
          component: { template: '<div>首页</div>' }
        }
      ]
    })

    var app = new Vue({
      router
    }).$mount('#app')
  </script>
</body>

</html>

14、路由基础

(1)Router 构建选项

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      redirect: '/home'
    }, {
      path: '/home',
      component: Home,
    }, {
      path: '/about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }
  ]
})

mode

常用的有hashhistory两种配置

history和hash都可以实现前端路由的功能,区别是前者基于url的pathname段,后者基于hash段。

前者:http://127.0.0.1:3000/article/num1

后者:http://127.0.0.1:3000/#/article/num1(不一定是这样,但#是少不了的)

这样的区别带来的直接问题就是当处于二级或多级路由状态时,刷新页面,前者会将当前路由发送到服务器(因为是pathname),而后者不会(因为是hash段)。

history模式原理:

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.js"></script>
</head>

<body>
  <input type="button" value="" id="btn">
  <div id="content"></div>
  <script>
    var id = 0

    function update(id) {
      $("#btn").attr("value", id);
    }

    function addHistory(id) {
      history.pushState({ "id": id }, "", "");
    }

    window.onload = function () {
      update(id)
      addHistory(id)
    }

    $('#btn').on('click', function () {
      id = id + 1
      update(id)
      addHistory(id)
    })

    // onpopstate可以监控state变化
    window.onpopstate = function (e) {
      if (history.state) {
        var state = history.state;
        update(state.id)
        id = state.id
      }
    }
  </script>
</body>

</html>


 

base

默认值: "/"

应用的基路径。例如,如果整个单页应用服务在 /app/ 下,然后 base 就应该设为 "/app/"

routes

  routes: [
    {
      path: '/',
      redirect: '/home'
    }, {
      path: '/home',
      component: Home,
    }, {
      path: '/about',
      component: () => import('./views/About.vue')
    }
  ]

(2)<router-link>

渲染成a链接,点击时会跳转链接

      <router-link to="/home" class="m-nav-item">Home</router-link>
      <router-link to="/about" class="m-nav-item">About</router-link>

(3)<router-view>

当点击<router-link>时,会跳转链接,链接和router配置里的链接匹配上时,把对应的组件显示在router-view里

<router-view/>

(4)Router实例方法

this.$router.push(`/detail/${id}`)

 当你点击 <router-link> 时,这个方法会在内部调用,所以说,点击 <router-link :to="..."> 等同于调用 router.push(...)

生命式 编程式
<router-link :to="..."> router.push(...)
// 字符串
router.push('home')

// 对象
router.push({ path: 'home' })

// 命名的路由
router.push({ name: 'user', params: { userId: '123' }})

// 带查询参数,变成 /register?plan=private
router.push({ path: 'register', query: { plan: 'private' }})
      this.$router.push(`/detail/${id}`)
      this.$router.push({ path: `/detail/${id}` })
      this.$router.push({ name: "detail", params: { id: id } });
      this.$router.push({ path: `/detail/${id}`, query: { id } });

(5)路由对象

  data() {
    return {
      id: this.$route.params.id
    }
  }

 (6)重定向

几种方式杂糅在一起了:

    {
      path: '/',   //匹配路由路径
      //redirect: '/home'   //重定向
      //redirect: { name: 'home' }
      redirect: (to) => {
        console.log(to)
        //return '/home'
        return {
          name: 'home'
        }
      }
    },

(7)别名

{ path: '/a', component: A, alias: '/b' }

(8)路由懒加载

    {
      path: '/about',
      name: 'about',
      // route level code-splitting
      // this generates a separate chunk (about.[hash].js) for this route
      // which is lazy-loaded when the route is visited.
      component: () => import(/* webpackChunkName: "about" */ './views/About.vue')
    }

15、动态路由

import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      redirect: '/home'
    }, {
      path: '/home',
      component: Home,
    }, {
      path: '/detail/:id',
      component: () => import('./views/Detail')
    }
  ]
})

动态路由列表跳转详情:

16、嵌套路由

路由中的path加`/`与否有什么区别?

假设你的顶层是/home,那么孩子中me等同于/home/me,而/me就是/me

在router-link里也是类似的:

      <router-link to="hebei">河北</router-link>
      <router-link to="shanxi">山西</router-link>
      <router-link to="/hebei">河北</router-link>
      <router-link to="/shanxi">山西</router-link>
import Vue from 'vue'
import Router from 'vue-router'
import Home from './views/Home.vue'
import NotFound from './views/NotFound'
import Index from './views/Index'
import Me from './views/Me'

Vue.use(Router)

export default new Router({
  mode: 'history',
  base: process.env.BASE_URL,
  routes: [
    {
      path: '/',
      redirect: '/home/index'
    }, {
      path: '/home',
      component: Home,
      children: [{
        path: 'index',
        component: Index
      }, {
        path: 'me',
        component: Me
      }]
    }, {
      path: '/detail/:id',
      component: () => import('./views/Detail')
    }, {
      path: '*',
      component: NotFound
    }
  ]
})

17、事件修饰符

<input v-on:keyup.enter="submit">

或者这样写也可以实现功能:

<input v-model="password" @keyup="handleEnter" type="password" placeholder="请输入密码"/>
    handleEnter(e) {
      if (e.keyCode === 13) {
        this.login()
      }
    }

18、输入框自动获取焦点

ref:

<input v-model="username" placeholder="请输入用户名" ref="username"/>
  mounted() {
    this.$refs.username.focus()
  }

或者使用原生js实现:

<input v-model="username" placeholder="请输入用户名" id="username"/>
  mounted() {
    document.getElementById('username').focus()
  }

19、props

props 可以是数组或对象,用于接收来自父组件的数据。props 可以是简单的数组,或者使用对象作为替代,对象允许配置高级选项,如类型检测、自定义验证和设置默认值。

第一种写法:

props: ['bookNav', 'currentIndex', 'onNav']

第二种写法:

  props: {
    bookNav: Array,
    currentIndex: {
      type: Number,
      default: 1
    },
    onNav: Function,
  },
  props: {
    // 检测类型
    height: Number,
    // 检测类型 + 其他验证
    age: {
      type: Number,
      default: 0,
      required: true,
      validator: function (value) {
        return value >= 0
      }
    }
  }

20、keep-alive

可以用在<router-view>上,切换路由时,保持之前的状态的,但是手动刷新后,状态会丢失

    <keep-alive>
      <router-view></router-view>
    </keep-alive>

21、禁用eslint

在vue.config.js里添加下面的配置:

    module.exports = {
        lintOnSave:false //禁用eslint语法验证功能
    }

22、计算属性缓存 vs 方法

我们可以将同一函数定义为一个方法而不是一个计算属性。两种方式的最终结果确实是完全相同的。然而,不同的是计算属性是基于它们的响应式依赖进行缓存的。只在相关响应式依赖发生改变时它们才会重新求值。而调用方法将总会再次执行函数。

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Vue</title>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>

<body>
  <div id="app">
    <div>
      {{count}},{{double}},{{double}},{{double}},
      {{doubleByMethods()}},{{doubleByMethods()}},{{doubleByMethods()}}
    </div>
  </div>

  <script>
    new Vue({
      el: '#app',
      data() {
        return {
          count: 1
        }
      },
      computed: {
        double() {
          console.log(1)
          return this.count * 2
        }
      },
      methods: {
        doubleByMethods() {
          console.log(2)
          return this.count * 2
        }
      }
    })
  </script>
</body>

</html>

23、watch监听属性

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <title>Vue</title>
  <script src="https://cdn.bootcss.com/vue/2.6.10/vue.min.js"></script>
</head>

<body>
  <div id="app">
    <div>
      {{count}},{{double}}
      <div>
        <button @click="handleAdd">加</button>
      </div>
    </div>
  </div>

  <script>
    new Vue({
      el: '#app',
      data() {
        return {
          count: 1,
          double: ''
        }
      },
      methods: {
        handleAdd() {
          this.count = this.count + 1
        }
      },
      watch: {
        count() {
          this.double = this.count * 2
        }
      }
    })
  </script>
</body>

</html>

24、缩写

参考链接:

https://cn.vuejs.org/v2/guide/syntax.html#%E7%BC%A9%E5%86%99

v-bind缩写:

<!-- 完整语法 -->
<a v-bind:href="url">...</a>

<!-- 缩写 -->
<a :href="url">...</a>

v-on缩写:

<!-- 完整语法 -->
<a v-on:click="doSomething">...</a>

<!-- 缩写 -->
<a @click="doSomething">...</a>

25、购物车

26、吸顶

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    .m-container {
      position: relative;
    }

    .m-sticky {
      position: sticky;
      top: 0px;
      height: 100px;
      background-color:#dddddd;
      font-size: 30px;
      text-align: center;
    }

    .m-content {
      line-height: 100px
    }
  </style>
</head>

<body>
  <div class="m-container">
    <p class="m-content">
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
    </p>
    <div class="m-sticky" id="m-sticky">吸顶</div>
    <p class="m-content">
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.</p>
  </div>
  <script src="https://cdn.bootcss.com/jquery/3.4.1/jquery.min.js"></script>
  <script>
    $('#m-sticky').on('click', (req, res) => {
      $('#m-sticky')[0].scrollIntoView(true)
    })
  </script>
</body>

</html>

17、盒模型

border和padding都会计算在宽度和高度之内:

box-sizing: border-box;

border和padding都不会计算在宽度和高度之内,这也是默认属性:

box-sizing: content-box;

18、单双排和模糊搜索

19、展开折叠和loading效果

20、通讯录

21、vuex

22、回到顶部

使用scrollIntoView:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <style>
    body {
      margin: 0;
    }

    .m-container {
      position: relative;
    }

    .m-content {
      line-height: 100px
    }

    .m-top-btn {
      position: fixed;
      bottom: 50px;
      right: 50px;
    }
  </style>

</head>

<body>
  <div class="m-sticky" id="m-top"></div>
  <div class="m-container">
    <p class="m-content">
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Pea horseradish azuki bean lettuce avocado asparagus okra. Kohlrabi radish okra azuki bean corn fava bean mustard
      tigernut jícama green bean celtuce.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.
      Grape silver beet collard greens avocado quandong fennel gumbo black-eyed pea watercress potato tigernut corn
      groundnut. Chickweed okra pea winter purslane coriander yarrow sweet pepper radish garlic brussels sprout
      groundnut summer purslane earthnut pea tomato spring onion azuki bean gourd. Gumbo kakadu plum komatsuna
      black-eyed pea green bean zucchini gourd winter purslane silver beet rock melon radish asparagus spinach.</p>
    <button class="m-top-btn" οnclick="handleTop()">回到顶部</button>
  </div>
  <script>
    function handleTop() {
      document.getElementById('m-top').scrollIntoView(true)
    }
  </script>
</body>

</html>

使用Element-UI:

<template>
  <div id="top" class="m-wrap">
    <div class="m-height">高度2000px</div>
    <el-backtop target="#top"></el-backtop>
  </div>
</template>

<script>
export default {};
</script>
.m-wrap{position: absolute;top: 0;left: 0;right: 0;bottom: 0;overflow-y: auto;}
.m-height{height: 2000px;}

23、如何上传npm包

1.先注册一个npm账号

2.在终端登录npm账号:npm login 回车输入用户名密码和邮箱

3.新建一个文件夹,cd到新创建的文件夹,使用npm init 生成package.json

4.使用npm publish上传npm包,你会收到一封邮件,在npm官网可以看到刚上传的npm包

24、git

1.创建项目

2.如何使用Git命令将项目从github或者服务器上克隆下来:

git clone git@github.com:baweireact/m-app-test.git

3.提交代码

添加要上传的文件:

git add README.md

提交到本地:

git commit -m "first commit"

提交到远程:

git push origin master

加入到暂存区参考链接:

http://www.softwhy.com/article-8489-1.html

git add readme.txt

git add readme.txt ant.txt

git add *.html

git add all 

git add .

git add *

git log

git status

git add . 会把本地所有untrack的文件都加入暂存区,并且会根据.gitignore做过滤,但是git add * 会忽略.gitignore把任何文件都加入 

git使用简易指南:

https://www.bootcss.com/p/git-guide/

git工作区:

::切换到develop分支
git checkout beta

git pull origin beta

::把develop分支的代码合并到beta上
git merge develop

git status

::push到远程beta
git push origin beta

::切换到develop分支 
git checkout develop

echo. & pause 

新建分支:

 git checkout -b feature_x

将分支提交到远程:

git push origin feature_x

切换分支:

git checkout master

查看有所有分支:

git branch -a

查看本地分支:

git branch

在github上查看有哪些分支:

删除本地分支:

git branch -d feature_x

删除远程分支:

git push origin -d feature_x

合并分支,合并后再提交到远程仓库(先切换到master分支,然后合并feature_login分支到master分支,然后再把合并后的代码提交到远程仓库):

git merge feature_login

git push origin master

对比分支:

git diff master feature_login

项目github源码

https://github.com/baweireact/m-vue-demo-base

原文地址:https://www.cnblogs.com/xutongbao/p/11915673.html