• 16-Vue-webpack



    typora-copy-images-to: media

    Webpack

    网页静态资源的问题

    1. 网页中常见静态资源
      • JS
        • .js .jsx .coffee .ts(TypeScript 类 C# 语言)
      • CSS
        • .css .less .sass .scss
      • Images
        • .jpg .png .gif .bmp .svg
      • 字体文件(Fonts)
        • .svg .ttf .eot .woff .woff2
      • 模板文件
        • .ejs .jade .vue【这是在webpack中定义组件的方式,推荐这么用】
    2. 网页中引入的静态资源多了以后有什么问题
      1. 网页加载速度慢, 因为我们要发起很多的二次请求
      2. 要处理错综复杂的依赖关系
    3. 如何解决上述两个问题
      1. 合并、压缩、精灵图、图片的Base64编码
      2. 可以使用之前学过的requireJS、也可以使用webpack可以解决各个包之间的复杂依赖关系

    webpack介绍

    1. 什么是webpack?

    webpack 是前端的一个项目构建工具,它是基于 Node.js 开发出来的一个前端工具

    1. 如何完美实现上述的2种解决方案
    • 使用Gulp, 是基于 task 任务的

    • 使用Webpack, 是基于整个项目进行构建的

    • 借助于webpack这个前端自动化构建工具,可以完美实现资源的合并、打包、压缩、混淆等诸多功能

    • 根据官网的图片介绍webpack打包的过程

    • webpack官网

    webpack安装

    ​ 有2种安装方式

    • npm i webpack -g - 全局安装webpack,这样就能在全局使用webpack的命令
    • npm i webpack --save-dev - 私有安装webpack,在项目根目录中运行安装到项目依赖中

    webpack入门案例

    初步使用webpack打包构建列表隔行变色案例

    步骤:

    1. npm init -初始化项目,使用npm管理项目中的依赖包
    2. 创建项目基本的目录结构
    3. npm i jquery --save - 安装jquery类库
    4. 创建main.js并书写各行变色的代码逻辑
    	// 导入jquery类库
        import $ from 'jquery'	// import from 是ES6中导入模块的方式
    
        // 设置偶数行背景色,索引从0开始,0是偶数
        $('#list li:even').css('backgroundColor','lightblue');
        // 设置奇数行背景色
        $('#list li:odd').css('backgroundColor','pink');
    
    1. 直接在页面上引用main.js会报错,因为浏览器不认识import这种ES6高级的JS语法,需要使用webpack进行处理,webpack默认会把这种高级的语法转换为低级的浏览器能识别的语法
    2. webpack 入口文件路径 输出文件路径 - 对main.js进行处理
    webpack src/js/main.js dist/bundle.js
    
    1. 效果

    webpack作用

    • webpack能够处理JS文件间的相互依赖关系
    • webpack能够处理JS的兼容问题,把高级的、浏览器不是别的语法,转为低级的、浏览器能正常识别的语法

    webpack配置文件-基础

    使用webpack的配置文件简化打包时候的命令

    1. 在项目根目录中创建webpack.config.js
    2. 由于运行webpack命令的时候,webpack需要指定入口文件和输出文件的路径,所以,我们需要在webpack.config.js配置这两个路径:
        // 导入处理路径的模块
        var path = require('path');
    
        // 导出一个配置对象,将来webpack在启动的时候,会默认来查找webpack.config.js,并读取这个文件中导出的配置对象,来进行打包处理
        module.exports = {
          	// 项目入口文件
            entry: path.resolve(__dirname, 'src/js/main.js'), 
          	// 配置输出选项  
          	output: { 
                path: path.resolve(__dirname, 'dist'), // 配置输出的路径
                filename: 'bundle.js' // 配置输出的文件名
            }
        }
    
    1. webpack - 简化后的命令

    webpack-dev-server实时打包构建

    由于每次重新修改代码之后,都需要手动运行webpack打包的命令,比较麻烦,所以使用webpack-dev-server来实现代码实时打包编译,当修改代码之后,会自动进行打包构建。

    安装:

    • npm i webpack-dev-server --save-dev - 本地项目安装开发依赖包
    • npm i webpack -D - webpack-dev-server这个工具,如果想要正常运行,需要在本地项目中安装 webpack

    使用:

    • 由于webpack-dev-server是在当前项目中安装的包,不能直接在终端powershell中直接运行命令

      直接运行webpack-dev-server 会出错

    • 需要在package.json中添加

      • --contentBase 入口文件目录: 指定启动根目录,在访问http://localhost:8080/的时候直接访问到index首页
      // package.js
      "scripts": {
        ...
        "dev": "webpack-dev-server",
        "dev2": "webpack-dev-server --open --port 3000 --contentBase src -- hot"
      }
      
    • 修改index页面中script的src属性为<script src="bundle.js"></script> , 因为通过webpack-dev-server生成的bundle.js位于/ 根目录下

      webpack-dev-server打包生成的bundle.js文件,并没有存放到实际的物理磁盘中,而是直接托管到内存中,所以在项目根目录中找不到打包好的bundle.js文件,这样做的好处是运行速度非常快

    • npm run dev - 在终端运行该命令,实现实时打包构建

    方式1:常用命令参数:

    package.js

    • "dev": "webpack-dev-server --open" - 打包后自动打开浏览器
    • "dev": "webpack-dev-server --port 3000" - 在3000端口运行
    • "dev": "webpack-dev-server --contentBase 入口文件目录" - 指定启动根目录,在访问http://localhost:8080/的时候直接访问到index首页
    • "dev": "webpack-dev-server --hot" - 启用热更新,可以不用重新生成整个bundle.js文件,只是以补丁的方式变动局部内容;实现CSS样式的无刷新变动

    方式2:在webpack.config.js中配置webpack-dev-server命令参数:

    // webpack.config.js
    const webpack = require('webpack');	// 启用热更新,第2步
    module.exports = {
      ...
      devServer: {
        open: true, // 自动打开浏览器
      	port: 3000, // 设置运行端口
        contentBase: 'src', // 指定托管的根目录
      	hot: true	// 启用热更新,第1步
      },
      // 配置插件的节点
      plugins: [
        new webpack.HotModuleReplacementPlugin() // 启用热更新,第3步, new一个热更新的模块对象
      ]
    }
    

    注意:webpack配置文件中,所有复数选项都是一个数组[]

    注意:热更新在JS中表现的不明显,可以从CSS身上进行介绍说明

    html-webpack-plugin插件配置启动页面

    由于使用--contentBase指令的过程比较繁琐,需要指定启动的目录,同时还需要修改index.html中script标签的src属性,所以推荐大家使用html-webpack-plugin插件配置启动页面。该插件还可以在内存中生成HTML页面

    安装:

    • cnpm i html-webpack-plugin --save-dev - 安装依赖包方式1

      cnpm i html-webpack-plugin -D - 安装依赖包方式2

    作用:

    • 自动在内存中根据指定的模板文件生成一个内存的页面
    • 自动把打包好的bundle.js追加到生成的页面中去

    使用:

    • 修改webpack.config.js配置文件如下
        // 导入处理路径的模块
        var path = require('path');
        // 导入自动生成HTMl文件的插件,第1步
    +    var htmlWebpackPlugin = require('html-webpack-plugin');
    
        module.exports = {
            entry: path.resolve(__dirname, 'src/js/main.js'), // 项目入口文件
            output: { // 配置输出选项
                path: path.resolve(__dirname, 'dist'), // 配置输出的路径
                filename: 'bundle.js' // 配置输出的文件名
            },
            plugins:[ // 添加plugins节点配置插件
              	 // new一个html插件对象,第2步
    +            new htmlWebpackPlugin({
    +                template:path.resolve(__dirname, 'src/index.html'),//指定的模板文件路径
    +                filename:'index.html'//自动生成的HTML文件的名称
    +            })
            ]
        }
    
    • 修改package.jsonscripts节点中的dev指令如下:
    "dev": "webpack-dev-server"
    
    • 将index.html中引入bundle.js的script标签注释掉,因为html-webpack-plugin插件会自动把bundle.js添加到index.html页面中!

    使用webpack打包css文件

    所需加载器:

    • style-loader
    • css-loader

    安装:

    • npm i style-loader css-loader --save-dev - 安装style-loader和css-loader依赖包

      npm i style-loader css-loader -D - 安装style-loader和css-loader依赖包

    使用:

    • 修改webpack.config.js这个配置文件

      	// 这个节点用于配置所有的第三方模块加载器
      	module: {
            	// 所有的第三方模块的匹配规则
              rules: [
                	// 匹配处理css文件所用到的第三方模块规则
                  { test: /.css$/, use: ['style-loader', 'css-loader'] }	
              ]
          }
      

      注意:use表示使用哪些模块来处理test所匹配到的文件;use相关loader模块的调用顺序是从后向前调用的

      注意:webpack默认只能打包处理JS类型的文件,无法处理其他类型的文件(包括CSS文件)

      注意:在webpack1.x版本中可以省略加载器后面的-loader, 但是2.x之后的版本必须带上 -loader

    • 现在可以直接在 mian.js 入口文件中导入css文件

      import './css/index.css'
      ...
      

    使用webpack打包less文件

    所需加载器:

    • style-loader
    • css-loader
    • less-loader
    • less

    安装:

    • npm i less-loader less -D - 安装less-loader依赖包
    • npm i less -D - 安装less依赖包,该依赖包是less-loader内部需要的

    配置:

    • 修改webpack.config.js配置文件
    // 匹配处理less文件所用到的第三方模块规则
    { test: /.less$/, use: ['style-loader', 'css-loader', 'less-loader'] },
    

    使用webpack打包sass文件

    所需加载器:

    • style-loader
    • css-loader
    • sass-loader
    • node-sass

    安装:

    • npm i sass-loader node-sass --save-dev - 安装sass-loader和node-sass依赖包

    配置:

    • webpack.config.js中添加处理sass文件的loader模块
    // 匹配处理sass文件所用到的第三方模块规则
    { test: /.scss$/, use: ['style-loader', 'css-loader', 'sass-loader'] }
    

    使用webpack处理css中的url路径

    注意:默认情况下,webpack无法处理CSS文件中的url地址,不管是图片还是字体库,只要是url地址都处理不了

    所需加载器:

    • url-loader
    • file-loader

    安装:

    • npm i url-loader --save-dev - 安装url-loader依赖包
    • npm i file-loader --save-dev - 安装file-loader依赖包,url-loader的内部依赖

    配置:

    • webpack.config.js中添加处理url路径的loader模块(默认图片格式是base64格式)

      // 匹配处理图片文件所用到的第三方模块规则
      { test: /.(png|jpg|gif|bmp|jpeg)$/, use: 'url-loader' }
      
    • 参数:limit - 可以通过limit指定进行base64编码的图片大小;只有小于指定字节(byte)的图片才会进行base64编码,大于等于不会转为base64编码

      { test: /.(png|jpg|gif|bmp|jpeg)$/, use: 'url-loader?limit=43960' }
      

      注意:这里的传参格式和网站url传参格式一样

    • 参数:name - 设置打包之后的图片名称(由于打包后的图片托管在/ 根目录下,所以设置了该参数后会造成图片重名

      // name=[name].[ext] 是固定写法,表示打包之后的图片名称不变,还是图片文件夹中的原名
      { test: /.(png|jpg|gif|bmp|jpeg)$/, use: 'url-loader?limit=43960&name=[name].[ext]' }
      
      // 打包后的图片名为: 8位的哈希值-原名.原扩展名
      { test: /.(png|jpg|gif|bmp|jpeg)$/, use: 'url-loader?limit=43960&name=[hash:8]-[name].[ext]' }
      

    使用:

    处理字体文件

    • webpack.config.js 中配置匹配的扩展名

      // 匹配处理字体文件所用到的第三方模块规则
      { test: /.(ttf|eot|svg|woff|woff2)$/, use: 'url-loader' }
      
    • main.js 中引入bootstrap字体文件css样式

      import 'bootstrap/dist/css/bootstrap.css'
      

      注意:如果要通过路径的形式去引入 node_modules 里的文件,可以直接省略路径中的 node_modules这一层目录,直接写包的名称,后面跟上具体的文件路径

      注意:在json文件中不能有注释,入package.json 中不能有注释符号

    使用babel处理高级ES语法

    在webpack中默认只能处理一部分 es6 新语法,一些更高级的es6语法或者es7语法webpack处理不了,这时候就需要借助第三方loader(babel)来帮助处理这些高级语法

    在webpack中有两套第三方包,来安装babel相关的loader功能(2套同时需要,并不是二选一):

    所需加载器:

    • babel-core
    • babel-loader
    • babel-plugin-transform-runtime
    • babel-preset-es2015(升级版:babel-preset-env)
    • babel-preset-stage-0

    安装:

    • cnpm i babel-core babel-loader babel-plugin-transform-runtime --save-dev - 第一套包:安装babel的相关loader包,转换工具
    • cnpm i babel-preset-es2015 babel-preset-stage-0 --save-dev - 第二套包:安装babel转换的语法,转换语法

    配置:

    • webpack.config.js中添加相关loader模块
    { test: /.js$/, use: 'babel-loader', exclude: /node_modules/ }
    

    ​ 注意*:一定要把node_modules文件夹添加到排除项,原因:

    ​ 1、 如果不排除 node_modules,则babel会把 node_modules 中所有的第三方js文件全部打包编译,这样会非常消耗CPU,打包速度也非常慢

    ​ 2、 即使babel 将node_modules中的所有js文件编译完毕,项目也无法正常运行

    • 在项目根目录中添加.babelrc文件,并修改这个配置文件如下:
    {
        // "presets":["es2015", "stage-0"],
        "presets":["env", "stage-0"],
        "plugins":["transform-runtime"]
    }
    
    • 注意:语法插件babel-preset-es2015可以更新为babel-preset-env,它包含了所有的ES相关的语法

    在普通页面中使用render函数渲染组件

    注意:return的结果会替换页面中el指定的容器,而使用components插入的组件不会替换掉整个el容器

        <div id="app">
        </div>
    
        <script>
            var login = {
              template: '<h3>这是登录组件</h3>'
            }
            var vm = new Vue({
                el: '#app',
                data: {
                    
                },
              	// createElements是一个方法,调用它能将指定的组件模板渲染为html结构
              	render: function(createElements){
                  // 注意:return的结果会替换页面中el指定的容器
                  return createElements(login);
              	}
            });
        </script>
    

    在webpack中配置.vue组件页面的解析

    默认webpack无法打包 .vue 文件,需要安装相关loader

    所需依赖包:

    • vue
    • vue-loader
    • vue-template-compiler
    • style-loader
    • css-loader

    安装:

    • npm i vue -S - 将vue安装为运行依赖,该包功能不完整,只提供了runtime-only的方式,并没有提供网页script中的使用方式
    • npm i vue-loader vue-template-compiler -D - 将解析转换vue的包安装为开发依赖
    • npm i style-loader css-loader -D - 将解析转换CSS的包安装为开发依赖,因为.vue文件中会写CSS样式

    配置:

    • webpack.config.js中,添加如下module规则:
    module: {
        rules: [
          { test: /.css$/, use: ['style-loader', 'css-loader'] },
          { test: /.vue$/, use: 'vue-loader' }
        ]
      }
    

    使用:

    • index.html页面

          <div id="app"></div>
      
    • 创建App.vue组件页面,由template、script、style三部分组成

      • export default - ES6中提供的导出模块的方式,向外暴露成员,在一个模块中只允许export default向外暴露一次

      • export - ES6中提供的导出模块的方式

        • 在一个模块中可以同时使用export 和export default 向外暴露成员

        • 使用export向外暴露的成员,只能使用 { } 的形式来接收,这种叫做按需导出

        • 使用export导出的成员,必须严格按照导出时候的名称来使用 { } 按需接收,或者使用 as 起别名

        • export可以向外暴露多个成员,同时如果某些成员我们在import时不需要,则不用写到 { }

          // 模块文件
          export var name = 'Tom';
          export var age = '20';
          
          // 引入文件
          import { name, age as age1 } from 模块文件;
          console.log(name + ' -- ' + age1);
          

      • import 模块名称 from '模块标识符' - ES6中提供的导入模块的方式

       <template>
          <!-- 注意:在 .vue 的组件中,template 中必须有且只有唯一的根元素进行包裹,一般都用 div 当作唯一的根元素 -->
          <div>
            <h1>这是APP组件 - {{msg}}</h1>
            <h3>我是h3</h3>
          </div>
        </template>
    
        <script>
        // 注意:在 .vue 的组件中,通过 script 标签来定义组件的行为,需要使用 ES6 中提供的 export default 方式,导出一个vue实例对象
        export default {
          // 组件中的data必须是一个function
          // data() {}	//ES6简写
          data: function() {
            return {
              msg: 'OK'
            }
          },
          methods: {
            show(){
              console.log('show方法');
            }
          }
        }
        </script>
    
        <style scoped>
        h1 {
          color: red;
        }
        </style>
    
    • 创建main.js入口文件
        // 导入 Vue 组件
        import Vue from 'vue'
    
        // 导入 App组件
        import App from './components/App.vue'
    
        // 创建一个 Vue 实例,使用 render 函数,渲染指定的组件
        var vm = new Vue({
          el: '#app',
          // render: c => c(App)  // ES6简写
          render: function(createElements){
            return createElements(App);
          }
        });
    

    在使用webpack构建的Vue项目中使用模板对象?

    1. webpack.config.js中添加resolve属性:
    resolve: {
        alias: {
          'vue$': 'vue/dist/vue.esm.js'
        }
      }
    

    总结 webpack 中如何使用vue

    1、 安装vue包

    npm i vue -S
    

    2、 由于在webpack中推荐使用 .vue 这个组件模板文件,所以需要安装能解析这种文件的loader

    npm i vue-loader vue-template-compiler -D
    

    3、 在main.js中导入vue模块

    import Vue from 'vue'
    

    4、 定义一个 .vue 结尾的组件,该组件由 template script style 三部分组成

    5、 在main.js中使用import导入这个组件

    import login from './login.vue'
    

    6、 创建vm实例

    var vm = new Vue({
    	el: '#app',
      	render: c => c(login);
    
    });
    

    7、 在index.html页面中创建一个id为app的div元素,作为vm实例控制的区域

    ES6中语法使用总结

    1. 使用 export defaultexport 导出模块中的成员; 对应ES5中的 module.exportsexport

    2. 使用 import ** from **import '路径' 还有 import {a, b} from '模块标识' 导入其他模块

    3. 使用箭头函数:(a, b)=> { return a-b; }

    结合webpack使用vue-router

    在vue组件页面中,集成vue-router路由模块

    vue-router官网

    1. 导入路由模块:
    import VueRouter from 'vue-router'
    
    1. 安装路由模块:
    Vue.use(VueRouter);
    
    1. 导入需要展示的组件:
    import login from './components/account/login.vue'
    import register from './components/account/register.vue'
    
    1. 创建路由对象:
    var router = new VueRouter({
      routes: [
        { path: '/', redirect: '/login' },
        { path: '/login', component: login },
        { path: '/register', component: register }
      ]
    });
    
    1. 将路由对象,挂载到 Vue 实例上:
    var vm = new Vue({
      el: '#app',
      // render: c => { return c(App) }
      render(c) {
        return c(App);
      },
      router // 将路由对象,挂载到 Vue 实例上
    });
    
    1. 改造App.vue组件,在 template 中,添加router-linkrouter-view
        <router-link to="/login">登录</router-link>
        <router-link to="/register">注册</router-link>
    
        <router-view></router-view>
    

    结合webpack实现children子路由

    组件中的style属性lang、scoped

    • lang属性:普通的style标签只支持普通的样式,如果想要启用scss或less,需要为style元素设置lang属性
    • scoped属性:该属性使得css的作用域只适用于当前组件以及子组件的部分
      • 样式的scoped,是通过css的属性选择器 div[data-v-56423] 来实现的
    <style lang="scss" scoped>
    body {
      div {
        font-style: italic
      }
    }
    </style>
    

    抽离路由为单独的模块

    使用 饿了么的 MintUI 组件

    Github 仓储地址

    Mint-UI官方文档

    1. 导入所有MintUI组件:
    import MintUI from 'mint-ui'
    
    1. 导入样式表:
    import 'mint-ui/lib/style.css'
    
    1. 在 vue 中使用 MintUI:
    Vue.use(MintUI)
    
    1. 使用的例子:
    <mt-button type="primary" size="large">primary</mt-button>
    

    使用 MUI 组件

    官网首页

    文档地址

    1. 导入 MUI 的样式表:
    import '../lib/mui/css/mui.min.css'
    
    1. webpack.config.js中添加新的loader规则:
    { test: /.(png|jpg|gif|ttf)$/, use: 'url-loader' }
    
    1. 根据官方提供的文档和example,尝试使用相关的组件

    MUI 和 MintUI的区别

    • MUI只是开发出来的一套好用的代码片段,里面提供了配套的样式、HTML代码,类似于Bootstrap;而MintUI是真正的组件库,是适用Vue技术封装出来的成套的组件,可以无缝和Vue项目进行集成开发
    • 从体验上来说,MintUI体验更好,以为这是别人帮我们开发好的现成的Vue组件;而MUI体验和Bootstrap类似
    • 任何项目都可以使用MUI或Bootstrap,但是MintUI只适用于Vue项目

    将项目源码托管到oschina中

    1. 点击头像 -> 修改资料 -> SSH公钥 如何生成SSH公钥

    2. 创建自己的空仓储,使用 git config --global user.name "用户名"git config --global user.email ***@**.com 来全局配置提交时用户的名称和邮箱

    3. 使用 git init 在本地初始化项目

    4. 使用 touch README.mdtouch .gitignore 来创建项目的说明文件和忽略文件;

    5. 使用 git add . 将所有文件托管到 git 中

    6. 使用 git commit -m "init project" 将项目进行本地提交

    7. 使用 git remote add origin 仓储地址将本地项目和远程仓储连接,并使用origin最为远程仓储的别名

    8. 使用 git push -u origin master 将本地代码push到仓储中

    App.vue 组件的基本设置

    1. 头部的固定导航栏使用 Mint-UIHeader 组件;

    2. 底部的页签使用 muitabbar;

    3. 购物车的图标,使用 icons-extra 中的 mui-icon-extra mui-icon-extra-cart,同时,应该把其依赖的字体图标文件 mui-icons-extra.ttf,复制到 fonts 目录下!

    4. 将底部的页签,改造成 router-link 来实现单页面的切换;

    5. Tab Bar 路由激活时候设置高亮的两种方式:

    • 全局设置样式如下:
    	.router-link-active{
         	color:#007aff !important;
       }
    
    • 或者在 new VueRouter 的时候,通过 linkActiveClass 来指定高亮的类:
    	// 创建路由对象
       var router = new VueRouter({
         routes: [
           { path: '/', redirect: '/home' }
         ],
         linkActiveClass: 'mui-active'
       });
    

    实现 tabbar 页签不同组件页面的切换

    1. 将 tabbar 改造成 router-link 形式,并指定每个连接的 to 属性;

    2. 在入口文件中导入需要展示的组件,并创建路由对象:

        // 导入需要展示的组件
        import Home from './components/home/home.vue'
        import Member from './components/member/member.vue'
        import Shopcar from './components/shopcar/shopcar.vue'
        import Search from './components/search/search.vue'
    
        // 创建路由对象
        var router = new VueRouter({
          routes: [
            { path: '/', redirect: '/home' },
            { path: '/home', component: Home },
            { path: '/member', component: Member },
            { path: '/shopcar', component: Shopcar },
            { path: '/search', component: Search }
          ],
          linkActiveClass: 'mui-active'
        });
    

    使用 mt-swipe 轮播图组件

    1. 假数据:
    lunbo: [
            'http://www.itcast.cn/images/slidead/BEIJING/2017440109442800.jpg',
            'http://www.itcast.cn/images/slidead/BEIJING/2017511009514700.jpg',
            'http://www.itcast.cn/images/slidead/BEIJING/2017421414422600.jpg'
          ]
    
    1. 引入轮播图组件:
    <!-- Mint-UI 轮播图组件 -->
        <div class="home-swipe">
          <mt-swipe :auto="4000">
            <mt-swipe-item v-for="(item, i) in lunbo" :key="i">
              <img :src="item" alt="">
            </mt-swipe-item>
          </mt-swipe>
        </div>
      </div>
    

    .vue组件中使用vue-resource获取数据

    1. 运行cnpm i vue-resource -S安装模块

    2. 导入 vue-resource 组件

    import VueResource from 'vue-resource'
    
    1. 在vue中使用 vue-resource 组件
    Vue.use(VueResource);
    

    相关文章

    babel-preset-env:你需要的唯一Babel插件
    Runtime transform 运行时编译es6

  • 相关阅读:
    30网络通信之多线程
    U盘自动拷贝
    多态原理探究
    应用安全
    应用安全
    编码表/转义字符/进制转换
    代码审计
    文件上传
    渗透测试-Web安全-SSRF
    中间人攻击
  • 原文地址:https://www.cnblogs.com/pikachu/p/14817368.html
Copyright © 2020-2023  润新知