鈴 创建的主题
  • PHP 209 0 1 发布
    全局变量$ACT

    DokuWiki根据「?do =」参数的指定的动作模式,对页面(文档)进行显示(show),编辑(edit),保存(save)等不同的处理。
    $ACT变量保存的就是上面这个动作模式值。

    show
    显示页面edit
    编辑页面preview
    预览页面save
    保存页面backlink
    反向链接revisions
    历史版本media
    媒体index
    sitemaprecent
    最近变更search
    检索

    例如,如果仅当在显示页面时才做部分处理,则可对动作模式进行判断,如下所示。

    <?php if($ACT == 'show') { // } ?> $conf

    这是一个数组变量,用于保存DokuWiki配置设置信息。 包括在DokuWiki的基本设置以及模板和插件等信息。

    $ID

    变量$ID保存页面名。 在以下名称空间和页面结构中,$ID保存的值则是example1。

    root └ ns1 └ ns1-1 └ example1 ns1:ns1-1:example1 $INFO

    它是一个数组变量,用于保存有关DokuWiki页面的信息。

    主要内容有

    id
    同$IDnamespace
    当前页面の名字空间isadmin
    是否管理员用户ismobile
    是否mobile终端外部链接Dokuwiki官方说明
  • 解析器 111 0 1 发布

    Benchpress是一个轻量级的超快速模板框架,运行于浏览器和node.js.

    安装

    Benchpress可以作为npm模块使用:

    npm i benchpressjs API

    Benchpress使用AOT )编译模型。 它要求在使用模板之前将模板预编译为Javascript模块。

    precompile.precompile(source, { minify = false, unsafe = false }): Promise<string>

    这个方法将模板源编译为Javascript代码,可以选择将结果压缩为 UglifyJS

    const benchpress=require('benchpressjs'); const template='My favourite forum software is {forum}. This templating engine is written in {language}.'; benchpress.precompile(template, {}).then((precompiled) => { // store it somewhere }); // precompiled output (function (factory) { if (typeof module==='object' && module.exports) { module.exports=factory(); } elseif (typeof define ==='function' && define.amd) { define(factory); } })(function () { function compiled(helpers, context, get, iter, helper) { return'My favourite forum software is '+get(context && context['forum']) +'. This templating engine is written in '+get(context && context['language']) +'.'; } return compiled; }); __express

    这个方法提供了一个快速引擎 API。

    <span></span>const express=require('express'); const app=express(); const benchpress=require('benchpressjs'); const data= { foo:'bar', }; app.configure(function() { app.engine('jst', benchpress.__express); app.set('view engine', 'jst'); app.set('views', 'path/to/compiled/templates'); }); app.render('myview', data, function(err, html) { console.log(html); }); app.get('/myroute', function(res, req, next) { res.render('myview', data); }); render.render(template, data): Promise<string>

    这个方法主要用于解析客户端上的模板。 要使用它,必须使用 .registerLoader(loader) 来设置用于获取已经编译模板模块的回调。

    require(['benchpress'], (benchpress) => { benchpress.registerLoader((name, callback) => { // fetch `name` template module }); benchpress.render('basic', { forum:'NodeBB', language:'Javascript', }).then((output) => { // do something with output }); }); 

    模板语法

    示例数据

    { "animals": [ { "name": "Cat", "species": "Felis silvestris catus", "isHuman": false, }, { "name": "Dog", "species": "Canis lupus familiaris", "isHuman": false, }, { "name": "Human", "species": "Homo sapiens", "isHuman": true } ], "package": { "name": "benchpressjs", "author": "psychobunny", "url": "http://www.github.com/benchpressjs/benchpress" }, "website": "http://burnaftercompiling.com", "sayHello": true } 简单键/值 My blog URL is {website}. The URL for this library is {{package.url}} 条件 <!-- IF sayHello --> Hello world! <!-- END --> <!-- IF !somethingFalse --> somethingFalse doesn't exist <!-- END --> 迭代

    重复HTML块。两个特殊键 @first 和 @last 可以用作布尔值,@index,@key 和 @value 特殊键也可以用。 Benchpress支持迭代对象,在这种情况下 @index 将是当前的循环号,@key 将是当前项的键。 对于普通数组,@key == @index。

    <!-- BEGIN animals --> {animals.name} is from the species {animals.species}. <!-- IF!animals.isHuman --> - This could be a pet. <!-- ENDIF!animals.isHuman --> <!-- END animals -->

    output:

    Cat is from the species Felis silvestris catus. - This could be a pet. Dog is from the Canis lupus familiaris. - This could be a pet. Human is from the species Homo sapiens. 帮助器

    帮助器是用于模板中高级逻辑的JavaScript方法。 这个例子展示了一个名为 print_is_human的函数的简单示例,它将根据 block的当前数据呈现文本。

    benchpress.registerHelper('print_is_human', function (data) { return (data.isHuman) ?"Is human":"Isn't human"; }); <!-- BEGIN animals --> {function.print_is_human} <!-- END animals -->

    output

    Isn't human Isn't human Is human

  • 开发工具 100 0 1 发布

    ebpack 是什么?

    本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(module bundler)。当 webpack 处理应用程序时,它会递归地构建一个依赖关系图(dependency graph),其中包含应用程序需要的每个模块,然后将所有这些模块打包成一个或多个 bundle。

     webpack快速了解几个基本的概念mode 开发模式

    webpack 提供 mode 配置选项,配置 webpack 相应模式的内置优化。

    // webpack.production.config.js module.exports = { + mode: 'production', } 入口文件(entry)

    入口文件,类似于其他语言的起始文件。比如:c 语言的 main 函数所在的文件。

    入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。

    可以在 webpack 的配置文件中配置入口,配置节点为: entry,当然可以配置一个入口,也可以配置多个。

    输出(output)

    output 属性告诉 webpack 在哪里输出它所创建的 bundles,以及如何命名这些文件。

    const path = require('path'); module.exports = { entry: './path/to/my/entry/file.js', output: { path: path.resolve(__dirname, 'dist'), filename: 'my-first-webpack.bundle.js' } }; loader

    loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。

    插件(plugins)

    loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。

    webpack 的安装

    请确保安装了 Node.js 的最新版本。而且已经在您的项目根目录下已经初始化好了最基本的package.json文件

    本地安装 webpack$ npm install --save-dev webpack # 如果你使用 webpack 4+ 版本,你还需要安装 CLI。 npm install --save-dev webpack-cli

    安装完成后,可以添加npm的script脚本

    // package.json "scripts": { "start": "webpack --config webpack.config.js" } 全局安装 webpack(不推荐)

    将使 webpack 在全局环境下可用:

    npm install --global webpack

    注意:不推荐全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中,可能会导致构建失败。

    快速入门完整 demo第一步:创建项目结构

    首先我们创建一个目录,初始化 npm,然后 在本地安装 webpack,接着安装 webpack-cli(此工具用于在命令行中运行 webpack):

    mkdir webpack-demo && cd webpack-demo npm init -y npm install webpack webpack-cli --save-dev

    项目结构

    webpack-demo + |- package.json + |- /dist + |- index.html + |- /src + |- index.js 第二步:安装 loadash 依赖和编写 js 文件npm install --save lodash

    编写:src/index.js 文件

    import _ from 'lodash'; function createDomElement() { let dom = document.createElement('div'); dom.innerHTML = _.join(['aicoder', '.com', ' wow'], ''); return dom; } document.body.appendChild(createDomElement());

    index.html

    <!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>起步</title> </head> <body> <script src="./main.js"></script> </body> </html> 第三步:编写 webpack 配置文件

    根目录下添加 webpack.config.js文件。

    webpack-demo |- package.json + |- webpack.config.js |- /dist |- index.html |- /src |- index.js

    webpack.config.js 内容如下:

    const path = require('path'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') } }; 执行构建任务

    直接执行构建任务:

    npx webpack

    打开: dist/index.html 可以查看到页面的结果。

    加载非 js 文件

    webpack 最出色的功能之一就是,除了 JavaScript,还可以通过 loader 引入任何其他类型的文件

    加载 CSS 文件第一步: 安装 css 和 style 模块解析的依赖 style-loader 和 css-loadernpm install --save-dev style-loader css-loader 第二步: 添加 css 解析的 loaderconst path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } }; css-loader: 辅助解析 js 中的 import './main.css'style-loader: 把 js 中引入的 css 内容 注入到 html 标签中,并添加 style 标签.依赖 css-loader

    你可以在依赖于此样式的 js 文件中 导入样式文件,比如:import './style.css'。现在,当该 js 模块运行时,含有 CSS 字符串的 <style> 标签,将被插入到 html 文件的 <head>中。

    第三步: 编写 css 文件和修改 js 文件

    在 src 目录中添加 style.css文件

    webpack-demo |- package.json |- webpack.config.js |- /dist |- bundle.js |- index.html |- /src + |- style.css |- index.js |- /node_modules

    src/style.css

    .hello { color: red; }

    修改 js 文件

    import _ from 'lodash'; + import './style.css'; function createDomElement() { let dom = document.createElement('div'); dom.innerHTML = _.join(['aicoder', '.com', ' wow'], ''); + dom.className = 'hello'; return dom; } document.body.appendChild(createDomElement());

    最后重新打开 dist 目录下的 index.html 看一下文字是否变成了红色的了。

    module 配置补充

    模块(module): 这些选项决定了如何处理项目中的不同类型的模块。

    webpack 模块可以支持如下:

    ES2015 import 语句CommonJS require() 语句AMD define 和 require 语句css/sass/less 文件中的 @import 语句。样式(url(...))或 HTML 文件(<img src=...>)中的图片链接(image url)module.noParse

    值的类型: RegExp | [RegExp] | function

    防止 webpack 解析那些任何与给定正则表达式相匹配的文件。忽略的文件中不应该含有 import, require, define 的调用,或任何其他导入机制。忽略大型的 library 可以提高构建性能。

    module.exports = { mode: 'devleopment', entry: './src/index.js', ... module: { noParse: /jquery|lodash/, // 从 webpack 3.0.0 开始,可以使用函数,如下所示 // noParse: function(content) { // return /jquery|lodash/.test(content); // } } ... }; module.rules

    创建模块时,匹配请求的规则数组。这些规则能够修改模块的创建方式。这些规则能够对模块(module)应用 loader,或者修改解析器(parser)。

    module.exports = { ... module: { noParse: /jquery|lodash/, rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } ... }; module RuleRule 条件详解字符串:匹配输入必须以提供的字符串开始。是的。目录绝对路径或文件绝对路径。正则表达式:test 输入值。函数:调用输入的函数,必须返回一个真值(truthy value)以匹配。条件数组:至少一个匹配条件。对象:匹配所有属性。每个属性都有一个定义行为。Rule.test{ test: Condition }:匹配特定条件。一般是提供一个正则表达式或正则表达式的数组,但这不是强制的。module.exports = { ... module: { rules: [ { test: /\.css$/, use: ['style-loader', 'css-loader'] } ] } ... };

    其他的条件比如:

    { include: Condition }:匹配特定条件。一般是提供一个字符串或者字符串数组,但这不是强制的。{ exclude: Condition }:排除特定条件。一般是提供一个字符串或字符串数组,但这不是强制的。{ and: [Condition] }:必须匹配数组中的所有条件{ or: [Condition] }:匹配数组中任何一个条件{ not: [Condition] }:必须排除这个条件module.exports = { ... module: { rules: [ { test: /\.css$/, include: [ path.resolve(__dirname, "app/styles"), path.resolve(__dirname, "vendor/styles") ], use: ['style-loader', 'css-loader'] } ] } ... }; Rule.use

    应用于模块指定使用一个 loader。

    Loaders can be chained by passing multiple loaders, which will be applied from right to left (last to first configured).

    加载器可以链式传递,从右向左进行应用到模块上。

    use: [ 'style-loader', { loader: 'css-loader' }, { loader: 'less-loader', options: { noIeCompat: true } } ];

    传递字符串(如:use: [ "style-loader" ])是 loader 属性的简写方式(如:use: [ { loader: "style-loader "} ])。

    加载 Sass 文件

    加载 Sass 需要sass-loader。

    安装

    npm install sass-loader node-sass webpack --save-dev

    使用:

    // webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader" }, { loader: "sass-loader" }] }] } };

    为 sass 文件注入内容:

    如果你要将 Sass 代码放在实际的入口文件(entry file)之前,可以设置 data 选项。此时 sass-loader 不会覆盖 data 选项,只会将它拼接在入口文件的内容之前。

    { loader: "sass-loader", options: { data: "$env: " + process.env.NODE_ENV + ";" } }

    注意:由于代码注入, 会破坏整个入口文件的 source map。 通常一个简单的解决方案是,多个 Sass 文件入口。

    创建 Source Map

    css-loader和sass-loader都可以通过该 options 设置启用 sourcemap。

    // webpack.config.js module.exports = { ... module: { rules: [{ test: /\.scss$/, use: [{ loader: "style-loader" }, { loader: "css-loader", options: { sourceMap: true } }, { loader: "sass-loader", options: { sourceMap: true } }] }] } }; PostCSS 处理 loader(附带:添加 css3 前缀)

    PostCSS是一个 CSS 的预处理工具,可以帮助我们:给 CSS3 的属性添加前缀,样式格式校验(stylelint),提前使用 css 的新特性比如:表格布局,更重要的是可以实现 CSS 的模块化,防止 CSS 样式冲突。

    我们常用的就是使用 PostCSS 进行添加前缀,以此为例:

    安装

    npm i -D postcss-loader npm install autoprefixer --save-dev # 以下可以不用安装 # cssnext可以让你写CSS4的语言,并能配合autoprefixer进行浏览器兼容的不全,而且还支持嵌套语法 $ npm install postcss-cssnext --save-dev # 类似scss的语法,实际上如果只是想用嵌套的话有cssnext就够了 $ npm install precss --save-dev # 在@import css文件的时候让webpack监听并编译 $ npm install postcss-import --save-dev const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: loader => [ require('autoprefixer')({ browsers: ['> 0.15% in CN'] }) // 添加前缀 ] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] } ] } }; 样式表抽离成专门的单独文件并且设置版本号

    首先以下的 css 的处理我们都把 mode 设置为 production。

    webpack4 开始使用: mini-css-extract-plugin插件, 1-3 的版本可以用: extract-text-webpack-plugin

    抽取了样式,就不能再用 style-loader注入到 html 中了。

    npm install --save-dev mini-css-extract-plugin const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const devMode = process.env.NODE_ENV !== 'production'; // 判断当前环境是开发环境还是 部署环境,主要是 mode属性的设置值。 module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, 'css-loader', 'postcss-loader', 'sass-loader' ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: devMode ? '[name].css' : '[name].[hash].css', // 设置最终输出的文件名 chunkFilename: devMode ? '[id].css' : '[id].[hash].css' }) ] };

    再次运行打包:

    在 dist 目录中已经把 css 抽取到单独的一个 css 文件中了。修改 html,引入此 css 就能看到结果了。

    压缩 CSS

    webpack5 貌似会内置 css 的压缩,webpack4 可以自己设置一个插件即可。

    压缩 css 插件:optimize-css-assets-webpack-plugin

    安装

    npm i -D optimize-css-assets-webpack-plugin const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }) ], optimization: { minimizer: [new OptimizeCSSAssetsPlugin({})] } }; JS 压缩

    压缩需要一个插件: uglifyjs-webpack-plugin, 此插件需要一个前提就是:mode: 'production'.

    安装

    npm i -D uglifyjs-webpack-plugin const path = require('path'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } }; 解决 CSS 文件或者 JS 文件名字哈希变化的问题

    HtmlWebpackPlugin插件,可以把打包后的 CSS 或者 JS 文件引用直接注入到 HTML 模板中,这样就不用每次手动修改文件引用了。

    安装

    npm install --save-dev html-webpack-plugin const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); const UglifyJsPlugin = require('uglifyjs-webpack-plugin'); const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: loader => [autoprefixer({ browsers: ['> 0.15% in CN'] })] } }, { loader: 'sass-loader' } ] } ] }, plugins: [ new MiniCssExtractPlugin({ filename: '[name][hash].css', chunkFilename: '[id][hash].css' }), new HtmlWebpackPlugin({ title: 'AICODER 全栈线下实习', // 默认值:Webpack App filename: 'main.html', // 默认值: 'index.html' template: path.resolve(__dirname, 'src/index.html'), minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true // 移除属性的引号 } }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } }; 清理 dist 目录

    每次构建,我们的 /dist 文件夹都会保存生成的文件,然后就会非常杂乱。

    通常,在每次构建前清理 /dist 文件夹,是比较推荐的做法

    clean-webpack-plugin 是一个比较普及的管理插件,让我们安装和配置下。

    npm install clean-webpack-plugin --save-dev

    webpack.config.js

    const path = require('path'); .... + const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, plugins: [ + new CleanWebpackPlugin(['dist']) ... ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } ... };

    现在执行 npm run build,再检查 /dist 文件夹。如果一切顺利,你现在应该不会再看到旧的文件,只有构建后生成的文件!

    加载图片与图片优化

    在 css 文件或者 sass 文件中添加如下代码

    $red: #900; $size: 20px; .box { height: 30px*2; font-size: $size; transform: translate3d( 0, 0, 0 ); + background: url('../static/1.jpeg') }

    运行打包发现如下错误:

    ERROR in ./src/static/1.jpeg 1:0 Module parse failed: Unexpected character '�' (1:0) You may need an appropriate loader to handle this file type.

    解决方案:file-loader处理文件的导入

    npm install --save-dev file-loader

    webpack.config.js

    const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, + { + test: /\.(png|svg|jpg|gif)$/, + use: [ + 'file-loader' + ] + } ] } };

    此时运行打包,发现 dist 目录多了一个图片文件,另外报错不再出现。

    那更进一步,图片如何进行优化呢?

    image-webpack-loader可以帮助我们对图片进行压缩和优化。

    npm install image-webpack-loader --save-dev

    使用:webpack.config.js

    const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ 'file-loader', + { + loader: 'image-webpack-loader', + options: { + mozjpeg: { + progressive: true, + quality: 65 + }, + optipng: { + enabled: false, + }, + pngquant: { + quality: '65-90', + speed: 4 + }, + gifsicle: { + interlaced: false, + }, + webp: { + quality: 75 + } + } + }, ] } ] } };

    此时在运行 webpack,发现会 生成的图片的大小会被压缩很多。

    更进一步处理图片成 base64

    url-loader功能类似于 file-loader,可以把 url 地址对应的文件,打包成 base64 的 DataURL,提高访问的效率。

    如何使用:

    npm install --save-dev url-loader

    webpack.config.js

    module.exports = { module: { rules: [ { test: /\.(png|svg|jpg|gif|jpeg|ico|woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', // 根据图片大小,把图片优化成base64 options: { limit: 10000 } }, { loader: 'image-webpack-loader', // 先进行图片优化 options: { mozjpeg: { progressive: true, quality: 65 }, optipng: { enabled: false }, pngquant: { quality: '65-90', speed: 4 }, gifsicle: { interlaced: false }, webp: { quality: 75 } } } ] } ] } }; 字体的处理(同图片)

    由于 css 中可能引用到自定义的字体,处理也是跟图片一致。

    const path = require('path'); module.exports = { entry: './src/index.js', output: { filename: 'bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.css$/, use: [ 'style-loader', 'css-loader' ] }, { test: /\.(png|svg|jpg|gif)$/, use: [ 'file-loader' ] }, + { + test: /\.(woff|woff2|eot|ttf|otf)$/, + use: [ + 'file-loader' + ] + } ] } }; 开发相关辅助js 使用 source map

    当 webpack 打包源代码时,可能会很难追踪到错误和警告在源代码中的原始位置。例如,如果将三个源文件(a.js, b.js 和 c.js)打包到一个 bundle(bundle.js)中,而其中一个源文件包含一个错误,那么堆栈跟踪就会简单地指向到 bundle.js。

    使用 inline-source-map 选项,这有助于解释说明 js 原始出错的位置。(不要用于生产环境):

    webpack.config.js

    const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, + devtool: 'inline-source-map', plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };  inline-source-map监控文件变化,自动编译。使用观察模式

    每次修改完毕后,都手动编译异常痛苦。最简单解决的办法就是启动watch。

    npx webpack --watch

    当然可以添加到 npm 的 script 中

    package.json

    { "name": "development", "version": "1.0.0", "description": "", "main": "webpack.config.js", "scripts": { "test": "echo \"Error: no test specified\" && exit 1", + "watch": "npx webpack --watch", "build": "npx webpack" }, "devDependencies": { "clean-webpack-plugin": "^0.1.16", "css-loader": "^0.28.4", "csv-loader": "^2.1.1", "file-loader": "^0.11.2", "html-webpack-plugin": "^2.29.0", "style-loader": "^0.18.2", "webpack": "^3.0.0", "xml-loader": "^1.2.1" } }

    但是有个 bug,就是每次我们修改 js 或者 css 文件后,要看到修改后的 html 的变化,需要我自己重新刷新页面。

    如何能不刷新页面,自动更新变化呢?

    使用 webpack-dev-server 和热更新

    webpack-dev-server 为你提供了一个简单的 web 服务器,并且能够实时重新加载(live reloading)。

    安装

    npm install --save-dev webpack-dev-server

    webpack.config.js

    const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); module.exports = { entry: { app: './src/index.js', print: './src/print.js' }, devtool: 'inline-source-map', + devServer: { + contentBase: './dist' + }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Development' }) ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } };

    启动此 webserver:

    webpack-dev-server --open

    官网其他配置

    devServer: { clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默认值) hot: true, // 启用 webpack 的模块热替换特性, 这个需要配合: webpack.HotModuleReplacementPlugin插件 contentBase: path.join(__dirname, "dist"), // 告诉服务器从哪里提供内容, 默认情况下,将使用当前工作目录作为提供内容的目录 compress: true, // 一切服务都启用gzip 压缩 host: '0.0.0.0', // 指定使用一个 host。默认是 localhost。如果你希望服务器外部可访问 0.0.0.0 port: 8080, // 端口 open: true, // 是否打开浏览器 overlay: { // 出现错误或者警告的时候,是否覆盖页面线上错误消息。 warnings: true, errors: true }, publicPath: '/', // 此路径下的打包文件可在浏览器中访问。 proxy: { // 设置代理 "/api": { // 访问api开头的请求,会跳转到 下面的target配置 target: "http://192.168.0.102:8080", pathRewrite: {"^/api" : "/mockjsdata/5/api"} } }, quiet: true, // necessary for FriendlyErrorsPlugin. 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。 watchOptions: { // 监视文件相关的控制选项 poll: true, // webpack 使用文件系统(file system)获取文件改动的通知。在某些情况下,不会正常工作。例如,当使用 Network File System (NFS) 时。Vagrant 也有很多问题。在这些情况下,请使用轮询. poll: true。当然 poll也可以设置成毫秒数,比如: poll: 1000 ignored: /node_modules/, // 忽略监控的文件夹,正则 aggregateTimeout: 300 // 默认值,当第一个文件更改,会在重新构建前增加延迟 } }

    如何启用热更新呢?

    webpack.config.js

    const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const CleanWebpackPlugin = require('clean-webpack-plugin'); + const webpack = require('webpack'); module.exports = { entry: { app: './src/index.js' }, devtool: 'inline-source-map', devServer: { contentBase: './dist', + hot: true }, plugins: [ new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'Hot Module Replacement' }), + new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依赖 + new webpack.HotModuleReplacementPlugin() // 替换插件 ], output: { filename: '[name].bundle.js', path: path.resolve(__dirname, 'dist') } }; JS启用babel转码

    虽然现代的浏览器已经兼容了96%以上的ES6的语法了,但是为了兼容老式的浏览器(IE8、9)我们需要把最新的ES6的语法转成ES5的。那么babel的loader就出场了。

    安装

    npm i -D babel-loader babel-core babel-preset-env

    用法

    在webpack的配置文件中,添加js的处理模块。

    module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快编译速度,不包含node_modules文件夹内容 use: { loader: 'babel-loader' } } ] }

    然后,在项目根目录下,添加babel的配置文件 .babelrc.

    .babelrc文件如下:

    { "presets": ["env"] }

    最后,在入口js文件中,添加ES6的❤新语法:

    class Temp { show() { console.log('this.Age :', this.Age); } get Age() { return this._age; } set Age(val) { this._age = val + 1; } } let t = new Temp(); t.Age = 19; t.show();

    最后打包:

    npx webpack

    最终打包后的js代码:

    var a = 1, b = 3, c = 9; console.log('a :', a); console.log('b :', b); console.log('c :', c); var Temp = function () { function Temp() { _classCallCheck(this, Temp); } _createClass(Temp, [{ key: 'show', value: function show() { console.log('this.Age :', this.Age); } }, { key: 'Age', get: function get() { return this._age; }, set: function set(val) { this._age = val + 1; } }]); return Temp; }(); var t = new Temp(); t.Age = 19; t.show(); Babel优化

    babel-loader可以配置如下几个options:

    cacheDirectory:默认值为 false。当有设置时,指定的目录将用来缓存 loader 的执行结果。之后的 webpack 构建,将会尝试读取缓存,来避免在每次执行时,可能产生的、高性能消耗的 Babel 重新编译过程(recompilation process)。如果设置了一个空值 (loader: 'babel-loader?cacheDirectory') 或者 true (loader: babel-loader?cacheDirectory=true),loader 将使用默认的缓存目录 node_modules/.cache/babel-loader,如果在任何根目录下都没有找到 node_modules 目录,将会降级回退到操作系统默认的临时文件目录。

    cacheIdentifier:默认是一个由 babel-core 版本号,babel-loader 版本号,.babelrc 文件内容(存在的情况下),环境变量 BABEL_ENV 的值(没有时降级到 NODE_ENV)组成的字符串。可以设置为一个自定义的值,在 identifier 改变后,强制缓存失效。

    forceEnv:默认将解析 BABEL_ENV 然后是 NODE_ENV。允许你在 loader 级别上覆盖 BABEL_ENV/NODE_ENV。对有不同 babel 配置的,客户端和服务端同构应用非常有用。

    注意:sourceMap 选项是被忽略的。当 webpack 配置了 sourceMap 时(通过 devtool 配置选项),将会自动生成 sourceMap。

    babel 在每个文件都插入了辅助代码,使代码体积过大.babel 对一些公共方法使用了非常小的辅助代码,比如 _extend。 默认情况下会被添加到每一个需要它的文件中。你可以引入 babel runtime 作为一个独立模块,来避免重复引入。

    安装:

    npm install babel-plugin-transform-runtime --save-dev npm install babel-runtime --save

    配置:

    webpack.config.js

    rules: [ // 'transform-runtime' 插件告诉 babel 要引用 runtime 来代替注入。 { test: /\.js$/, exclude: /(node_modules|bower_components)/, use: { loader: 'babel-loader', } } ]

    修改.babelrc

    { "presets": ["env"], "plugins": [ ["transform-runtime", { "helpers": true, "polyfill": true, "regenerator": true, "moduleName": "babel-runtime" }] ] }

    此时,webpack打包的时候,会自动优化重复引入公共方法的问题。

    ESLint校验代码格式规范

    安装

    npm install eslint --save-dev npm install eslint-loader --save-dev # 以下是用到的额外的需要安装的eslint的解释器、校验规则等 npm i -D babel-eslint standard

    使用

    // webpack.config.js module.exports = { // ... module: { rules: [ { test: /\.js$/, exclude: /node_modules/, loader: "eslint-loader", options: { // eslint options (if necessary) fix: true } }, ], }, // ... }

    eslint配置可以直接放到webpack的配置文件中,也可以直接放到项目根目录的 .eslintrc中文档

    // .eslintrc.js // https://eslint.org/docs/user-g... module.exports = { root: true, parserOptions: { parser: 'babel-eslint' }, env: { browser: true }, extends: [ // https://github.com/standard/st... 'standard' ], globals: { NODE_ENV: false }, rules: { // allow async-await 'generator-star-spacing': 'off', // allow debugger during development 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off', // 添加,分号必须 semi: ['error', 'always'], 'no-unexpected-multiline': 'off', 'space-before-function-paren': ['error', 'never'], // 'quotes': ["error", "double", { "avoidEscape": true }] quotes: [ 'error', 'single', { avoidEscape: true } ] } };

    此时eslint的配置就结束了。

    到此为止,一个完整的开发阶段的webpack的配置文件const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const CleanWebpackPlugin = require('clean-webpack-plugin'); const autoprefixer = require('autoprefixer'); const webpack = require('webpack'); module.exports = { mode: 'development', entry: './src/index.js', output: { filename: 'main.js', path: path.resolve(__dirname, './dist') }, devtool: 'inline-source-map', devServer: { clientLogLevel: 'warning', // 可能的值有 none, error, warning 或者 info(默认值) hot: true, // 启用 webpack 的模块热替换特性, 这个需要配合: webpack.HotModuleReplacementPlugin插件 contentBase: path.join(__dirname, "dist"), // 告诉服务器从哪里提供内容, 默认情况下,将使用当前工作目录作为提供内容的目录 compress: true, // 一切服务都启用gzip 压缩 host: '0.0.0.0', // 指定使用一个 host。默认是 localhost。如果你希望服务器外部可访问 0.0.0.0 port: 8085, // 端口 open: true, // 是否打开浏览器 overlay: { // 出现错误或者警告的时候,是否覆盖页面线上错误消息。 warnings: true, errors: true }, publicPath: '/', // 此路径下的打包文件可在浏览器中访问。 proxy: { // 设置代理 "/api": { // 访问api开头的请求,会跳转到 下面的target配置 target: "http://192.168.0.102:8080", pathRewrite: { "^/api": "/mockjsdata/5/api" } } }, quiet: true, // necessary for FriendlyErrorsPlugin. 启用 quiet 后,除了初始启动信息之外的任何内容都不会被打印到控制台。这也意味着来自 webpack 的错误或警告在控制台不可见。 watchOptions: { // 监视文件相关的控制选项 poll: true, // webpack 使用文件系统(file system)获取文件改动的通知。在某些情况下,不会正常工作。例如,当使用 Network File System (NFS) 时。Vagrant 也有很多问题。在这些情况下,请使用轮询. poll: true。当然 poll也可以设置成毫秒数,比如: poll: 1000 ignored: /node_modules/, // 忽略监控的文件夹,正则 aggregateTimeout: 300 // 默认值,当第一个文件更改,会在重新构建前增加延迟 } }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快编译速度,不包含node_modules文件夹内容 use: [{ loader: 'babel-loader' },{ loader: 'eslint-loader', options: { fix: true } }] }, { test: /\.(sa|sc|c)ss$/, use: [ 'style-loader', { loader: 'css-loader', options: { sourceMap: true } }, { loader: 'postcss-loader', options: { ident: 'postcss', sourceMap: true, plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})] } }, { loader: 'sass-loader', options: { sourceMap: true } } ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } } ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } }, { loader: 'image-webpack-loader', options: { mozjpeg: { progressive: true, quality: 65 }, optipng: { enabled: false }, pngquant: { quality: '65-90', speed: 4 }, gifsicle: { interlaced: false }, webp: { quality: 75 } } } ] } ] }, plugins: [ new MiniCssExtractPlugin({filename: '[name].css', chunkFilename: '[id].css'}), new CleanWebpackPlugin(['dist']), new webpack.NamedModulesPlugin(), // 更容易查看(patch)的依赖 new webpack.HotModuleReplacementPlugin(), // 替换插件 new HtmlWebpackPlugin({ title: 'AICODER 全栈线下实习', // 默认值:Webpack App filename: 'index.html', // 默认值: 'index.html' minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true, // 移除属性的引号 }, template: path.resolve(__dirname, 'src/index.html') }) ], optimization: {} };

    用于生产环境的配置

    const path = require('path'); const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require("mini-css-extract-plugin"); const UglifyJsPlugin = require("uglifyjs-webpack-plugin"); const OptimizeCSSAssetsPlugin = require("optimize-css-assets-webpack-plugin"); const CleanWebpackPlugin = require('clean-webpack-plugin'); const autoprefixer = require('autoprefixer'); module.exports = { mode: 'production', entry: './src/index.js', output: { filename: 'main.[hash].js', path: path.resolve(__dirname, './dist') }, module: { rules: [ { test: /\.js$/, exclude: /(node_modules)/, // 加快编译速度,不包含node_modules文件夹内容 use: [{ loader: 'babel-loader' },{ loader: 'eslint-loader', options: { fix: true } }] }, { test: /\.(sa|sc|c)ss$/, use: [ MiniCssExtractPlugin.loader, { loader: 'css-loader' }, { loader: 'postcss-loader', options: { ident: 'postcss', plugins: (loader) => [autoprefixer({browsers: ['> 0.15% in CN']})] } }, { loader: 'sass-loader' } ] }, { test: /\.(woff|woff2|eot|ttf|otf)$/, use: [ { loader: 'url-loader', options: { limit: 10000 } } ] }, { test: /\.(png|svg|jpg|gif|jpeg|ico)$/, use: [ 'file-loader', { loader: 'image-webpack-loader', options: { mozjpeg: { progressive: true, quality: 65 }, optipng: { enabled: false }, pngquant: { quality: '65-90', speed: 4 }, gifsicle: { interlaced: false }, webp: { quality: 75 } } } ] } ] }, plugins: [ new MiniCssExtractPlugin({filename: '[name][hash].css', chunkFilename: '[id][hash].css'}), new CleanWebpackPlugin(['dist']), new HtmlWebpackPlugin({ title: 'AICODER 全栈线下实习', // 默认值:Webpack App filename: 'index.html', // 默认值: 'index.html' template: path.resolve(__dirname, 'src/index.html'), minify: { collapseWhitespace: true, removeComments: true, removeAttributeQuotes: true, // 移除属性的引号 } }) ], optimization: { minimizer: [ new UglifyJsPlugin({ cache: true, parallel: true, sourceMap: true // set to true if you want JS source maps }), new OptimizeCSSAssetsPlugin({}) ] } }; 相关的loader列表

    webpack 可以使用 loader 来预处理文件。这允许你打包除 JavaScript 之外的任何静态资源。你可以使用 Node.js 来很简单地编写自己的 loader。

    文件raw-loader 加载文件原始内容(utf-8)val-loader 将代码作为模块执行,并将 exports 转为 JS 代码url-loader 像 file loader 一样工作,但如果文件小于限制,可以返回 data URLfile-loader 将文件发送到输出文件夹,并返回(相对)URLJSONjson-loader 加载 JSON 文件(默认包含)json5-loader 加载和转译 JSON 5 文件cson-loader 加载和转译 CSON 文件转换编译(Transpiling)script-loader 在全局上下文中执行一次 JavaScript 文件(如在 script 标签),不需要解析babel-loader 加载 ES2015+ 代码,然后使用 Babel 转译为 ES5buble-loader 使用 Bublé 加载 ES2015+ 代码,并且将代码转译为 ES5traceur-loader 加载 ES2015+ 代码,然后使用 Traceur 转译为 ES5ts-loader 或 awesome-typescript-loader 像 JavaScript 一样加载 TypeScript 2.0+coffee-loader 像 JavaScript 一样加载 CoffeeScript模板(Templating)html-loader 导出 HTML 为字符串,需要引用静态资源pug-loader 加载 Pug 模板并返回一个函数jade-loader 加载 Jade 模板并返回一个函数markdown-loader 将 Markdown 转译为 HTMLreact-markdown-loader 使用 markdown-parse parser(解析器) 将 Markdown 编译为 React 组件posthtml-loader 使用 PostHTML 加载并转换 HTML 文件handlebars-loader 将 Handlebars 转移为 HTMLmarkup-inline-loader 将内联的 SVG/MathML 文件转换为 HTML。在应用于图标字体,或将 CSS 动画应用于 SVG 时非常有用。样式style-loader 将模块的导出作为样式添加到 DOM 中css-loader 解析 CSS 文件后,使用 import 加载,并且返回 CSS 代码less-loader 加载和转译 LESS 文件sass-loader 加载和转译 SASS/SCSS 文件postcss-loader 使用 PostCSS 加载和转译 CSS/SSS 文件stylus-loader 加载和转译 Stylus 文件清理和测试(Linting && Testing)mocha-loader 使用 mocha 测试(浏览器/NodeJS)eslint-loader PreLoader,使用 ESLint 清理代码jshint-loader PreLoader,使用 JSHint 清理代码jscs-loader PreLoader,使用 JSCS 检查代码样式coverjs-loader PreLoader,使用 CoverJS 确定测试覆盖率框架(Frameworks)vue-loader 加载和转译 Vue 组件polymer-loader 使用选择预处理器(preprocessor)处理,并且 require() 类似一等模块(first-class)的 Web 组件angular2-template-loader 加载和转译 Angular 组件Awesome 更多第三方 loader,查看 awesome-webpack 列表other

    webpack还是有很多其他需要学习的内容。 请参考官网,或者研究一下vue-cli的生成的webpack的相关配置,也很值得学习。

    另外其他脚手架生成的相关配置都可以研究一下比如:create-react-app、yo等

    转载:https://www.cnblogs.com/fly_dragon/p/9397919.html

  • Vue 84 0 1 发布

    8.1. 前置知识学习npm 学习官方文档推荐资料npm入门npm介绍需要了解的知识点

    package.json

    文件相关配置选项

    npm 本地安装、全局安装、本地开发安装等区别及相关命令

    npm script脚步的基本编写能力

    有时间专门写一个这样的专题,如果需要可以邮件我。malun666@126.com

    webpack基础学习

    官方文档Webpack了解的知识点:webpack的基本配置了解webpack常用的loader: less-loader、sass-loader、 vue-loader、style-loader、css-loader、eslint-loader、babel-loader等熟悉Webpack的webpack-dev-server的基本配置和使用,会配置热更新

    es6语法学习

    阮一峰老师的大作《ECMAScript 6 入门》需要学习的知识点es6的模块机制各种最新语法糖,简写、geter、setter、箭头函数类、继承的心机制 ...

    nodejs基础知识

    知识点学习:文件操作http服务express

    组件化开发思想

    8.2. Vue脚手架工具:vue-cli构建实战项目

    其实如果编写Vue的前端项目,直接使用vue的官方vue-cli构建工具最好用,一个命令就可以直接生成项目的结构和目录。 而且官方需要依赖的包也可以自动配置好,只需要npm instal一下,然后就可以开发测试了。

    8.2.1. vue-cli安装# 安装vue-cli。 安装之前首先确保你已经安装好了nodejs 而且打开命令行 $ npm install -g vue-cli # 校验一下是否安装成功 $ vue -V #=> 2.5.1 我当前的版本是这个,你的可能比这个新 8.2.2. 使用vue-cli初始化项目

    通过以下命令的方式可以创建一个项目文件夹,并初始化对应的文件。

    $ vue init <template-name> <project-name>

    其中template-name可以取以下值,每个值对应不同的项目构建的模板。

    webpack--全功能的Webpack + vueify,包括热加载,静态检测,单元测试webpack-simple--一个简易的Webpack + vueify,以便于快速开始。browserify--全功能的Browserify + vueify,包括热加载,静态检测,单元测试browserify-simple--一个简易的Browserify + vueify,以便于快速开始。

    安装和开发控制台的命令:

    # 如果已经安装,请省略 $ npm install -g vue-cli # 初始化一个webpack全功能包的vue项目,请您把my-project换成你自己的项目名。 $ vue init webpack my-project # 用命令行进入当前项目目录 $ cd my-project # npm安装所有的依赖的包 $ npm install # 运行测试的首页 $ npm run dev 8.2.3. 综合实例开发记录通过vue-cli构建工具初始化项目目录

    安装过程,控制台会问你项目名称是什么?项目描述?项目作者,是否使用eslint校验,是否使用单元测试等....

    我的安装过程如下请参考:

    $ vue init webpack ttl # => 安装webpack模板的项目 ttl(项目名可以随便取) A newer version of vue-cli is available. latest: 2.6.0 installed: 2.5.1 This will install Vue 2.x version of template. For Vue 1.x use: vue init webpack#1.0 ttl ? Project name mydemovue # => 项目名称 ? Project description A Vue.js project # => 项目描述 ? Author malun <malun666@126.com> # => 作者 ? Vue build standalone # => 是否支持单文件组件 ? Use ESLint to lint your code? Yes # => 是否支持ESLint代码校验 ? Pick an ESLint preset Standard # => 校验的标准是什么? ? Setup unit tests with Karma + Mocha? Yes # => 是否使用单元测试 ? Setup e2e tests with Nightwatch? Yes # => 是否使用e2e测试 vue-cli · Generated "ttl". To get started: cd ttl npm install npm run dev Documentation can be found at https://vuejs-templates.github...

    通过上面一系列的命令后,我们就会创建一个webpack配置好的项目包。目录结构如下:

    ttl |--.babelrc #=> babel的配置文件,主要用于转换es6等最新的js语法。 |--.editorconfig #=> 编辑器配置 |--.eslintignore #=> eslint的忽略校验的配置文件 |--.eslintrc.js #=> eslint的配置校验js是否规范的配置文件 |--.gitignore #=> 设置git忽略的管理的文件 |--README.md #=> readme说明文件 |--build/ #=> 自动构建存放的文件地方 |--config/ #=> 当前开发、测试等配置的文件,需要懂点nodejs了啊。 |--index.html #=> 项目的主入口的模板 |--package.json #=> npm的配置文件 |--src/ #=> 源码目录 |--static/ #=> 静态资源存放的目录 |--test/ #=> 测试相关目录 初始化依赖包$ cd ttl #=> 进入上面创建好的项目目录 $ npm install #=> 安装所有的依赖包。 安装过程可能非常长,网络也可能有问题,请耐心等待。 # 安装完成后,可以直接运行测试,如果自动打开浏览器,并跳转到http://localhost:8080/ 说明一切都ok了。 $ npm run dev

    npm 安装的时候经常网会断开,国内的网(哎,说多了都是泪)你懂的。最好能科学上网,或者是用淘宝的npm的镜像

    安装vue-router组件$ npm i -S vue-router

    到项目的

    /src/components/

    目录下创建三个组件文件。

    分别是:

    首页组件

    <template> <div class="home"> <h3>{{ msg }}</h3> </div> </template> <script> export default { name: 'home', // 组件可以有自己的名字。 data () { // 组件的data必须是函数 return { msg: '这里是Home视图' } } } </script> <style scoped> h3 { background-color: #82c; } </style>

    用户首页组件

    <template> <div> <h3>{{ msg }}</h3> </div> </template> <script> export default { // es6的模块导出定义语法,此模块导出默认的对象 name: 'user', // 组件可以有自己的名字。 data () { // 组件的data必须是函数 return { msg: '这里是User视图' } } } </script> <style scoped> h3 { background-color: red; } </style>

    产品组件

    <template> <div class="product"> <h3>{{ msg }}</h3> </div> </template> <script> export default { name: 'product', // 组件可以有自己的名字。 data () { // 组件的data必须是函数 return { msg: '这里是Home视图' } } } </script> <style scoped> h3 { background-color: green; } </style>

    项目的目录结构为:

    ttl |-- src |--|-- Hello.vue |--|-- Home.vue |--|-- Product.vue |--|-- User.vue 创建router对象及配置路由

    在src目录下创建approuter.js文件。 然后添加如下代码:

    import VueRouter from 'vue-router' // 导入路由模块 import Home from './components/Home.vue' // 导入Home组件 import User from './components/User.vue' import Product from './components/Product.vue' export default new VueRouter({ // 定义路由规则对象 routes: [ {path: '/home', component: Home}, {path: '/user/:id', component: User}, {path: '/product/:id', component: Product} ] }) 修改main.js文件 找到src/目录下的main.js文件, 共修改4处,添加路由引用、添加路由规则对象导入、启用路由、将路由键入到Vue中。
    修改此文件为:// The Vue build version to load with the `import` command // (runtime-only or standalone) has been set in webpack.base.conf with an alias. import Vue from 'vue' import App from './App' import VueRouter from 'vue-router' // +++1、导入路由组件 import router from './approuter' // +++2、导入我们自己写的路由配置文件 // +++ 3、使用路由组件 Vue.use(VueRouter) /* eslint-disable no-new */ new Vue({ el: '#app', template: '<App/>', components: { App }, router: router // +++4、添加路由对象 }) 在app.vue中添加路由导航

    最终代码如下:

    <template> <div id="app"> <nav class="top-menu"> <ul > <li v-for="item in menuList"> <router-link :to="item.url">{{ item.name }}</router-link> </li> </ul> </nav> <hr> <div> <router-view></router-view> </div> </div> </template> <script> export default { name: 'app', data: function () { return { menuList: [ { name: '首页', url: '/home' }, { name: '用户', url: '/user/19' }, { name: '产品', url: '/product/20' } ] } } } </script> <style> #app { } .top-menu ul, .top-menu li { list-style: none; } .top-menu { overflow: hidden; } .top-menu li { float: left; width: 100px; } </style>  

    刷新一下浏览器,你将会看到最终的运行结果。


    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 62 0 1 发布

    7.1. Vue-router的版本对应

    注意: vue-router@2.x 只适用于 Vue 2.x 版本。 vue-router@1.x 对应于Vue1.x版本。

    的Github地址:vue-router文档地址7.2. vue-router的安装使用CDN连接方式

    https://unpkg.com/vue-router/dist/vue-router.js

    npm 安装npm install vue-router 7.3. vue-router 入门demo

    vue-router开发的步骤:

    第一步: 引入vue和vue-router包。

    可以使用cdn的方式或者npm的方式。如果配合npm和webpack的话可以直接作为一个模块导入即可。但是作为初学入门的话建议还是 直接使用cdn包的形式,先学会怎么用路由。

    <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>

    第二步: 定义路由跳转的组件

    // 1\. 定义(路由)组件。 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' }

    第三步: 定义路由规则对象

    // 每个路由path应该映射一个组件。 其中"component" 可以是 // 通过 Vue.extend() 创建的组件构造器, // 或者,只是一个组件配置对象。 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 创建路由对象 const router = new VueRouter({ routes // (缩写)相当于 routes: routes,es6的新语法 }) 第四步: 创建Vue对象,并加重上面创建的路由对象// 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') 第五步: 在模板中编写路由跳转链接<div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div>

    最终的代码:

    <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <div id="app"> <h1>Hello App!</h1> <p> <!-- 使用 router-link 组件来导航. --> <!-- 通过传入 `to` 属性指定链接. --> <!-- <router-link> 默认会被渲染成一个 `<a>` 标签 --> <router-link to="/foo">Go to Foo</router-link> <router-link to="/bar">Go to Bar</router-link> </p> <!-- 路由出口 --> <!-- 路由匹配到的组件将渲染在这里 --> <router-view></router-view> </div> <script> // 1\. 定义(路由)组件。 // 可以从其他文件 import 进来 const Foo = { template: '<div>foo</div>' } const Bar = { template: '<div>bar</div>' } // 2\. 定义路由 const routes = [ { path: '/foo', component: Foo }, { path: '/bar', component: Bar } ] // 3\. 创建 router 实例,然后传 `routes` 配置 // 你还可以传别的配置参数, 不过先这么简单着吧。 const router = new VueRouter({ routes // (缩写)相当于 routes: routes }) // 4\. 创建和挂载根实例。 // 记得要通过 router 配置参数注入路由, // 从而让整个应用都有路由功能 const app = new Vue({ router }).$mount('#app') </script> 7.4. 使用vue-router的综合实例

    下面是一个综合的例子, 页面上有几个导航的按钮,然后通过点击不同的按钮,可以在当前页面切换不同的组件。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之extend全局方法</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> <style> ul, li { list-style: none; } ul { overflow: hidden; } li { float: left; width: 100px; } h2 { background-color: #903;} </style> </head> <body> <div id="app"> <top-bar> </top-bar> <hr> <p>email to: {{ email }}</p> <hr> <router-view class="view one"></router-view> <footer-bar></footer-bar> </div> <script> var topbarTemp = ` <nav> <ul> <li v-for="item in NavList"> <router-link :to="item.url">{{ item.name }}</router-link> </li> </ul> </nav> `; // 定义组件:topbar Vue.component('top-bar', { template: topbarTemp, data: function () { return { NavList: [ { name: '首页', url: '/home'}, { name: '产品', url: '/product'}, { name: '服务', url: '/service'}, { name: '关于', url: '/about'} ] } } }); Vue.component('footer-bar', { // 定义组件 footerbar template: ` <footer> <hr/> <p>版权所有@flydragon<p> </footer> ` }); // 创建home模块 var home = { template: `<div> <h2>{{ msg }}<h2></div>`, data: function () { return { msg: 'this is home view' } } }; // 创建product 模块 var product = { template: `<div> {{ msg }}</div>`, data: function () { return { msg: 'this is product view' } } } // 定义路由对象 var router = new VueRouter({ routes: [ { path: '/home', component: home }, { path: '/product', component: product } ] }); // 初始化一个Vue实例 var app = new Vue({ el: '#app', data: { email: 'flydragon@gmail.com' }, router: router }); </script> </body> </html> 7.5. 路由参数获取

    定义路由路径的时候,可以指定参数。参数需要通过路径进行标识:/user/:id就是定义了一个规则,/user开头,然后后面的就是id参数的值。 比如:

    路由规则: /user/:id /user/9 => id = 9 /user/8 => id = 8 /user/1 => id = 1

    然后在跳转后的vue中可以通过this.$route.params.参数名获取对应的参数。 比如代码:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之extend全局方法</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script> </head> <body> <div id="app"> <nav> <router-link to="/user/9">用户</router-link> <router-link to="/stu/malun">学生</router-link> <hr> </nav> <router-view></router-view> </div> <script> var user = { template: ` <div>user id is : {{ $route.params.id }}</div> ` }; var stu = { template: ` <div> <h2>{{ getName }}</h2> </div> `, computed: { getName: function () { return this.$route.params.name; } } }; var router = new VueRouter({ routes: [ { path: '/user/:id', component: user }, { path: '/stu/:name', component: stu } ] }); var app = new Vue({ el: '#app', router: router }); </script> </body> </html> 7.6. js控制路由跳转

    上面我们演示的都是通过router-link进行跳转。 其实我们还可以通过js编程的方式进行路由的跳转。

    // 当前路由的view跳转到 /home router.push('home') // 对象, 跳转到/home router.push({ path: 'home' }) // 命名的路由 router.push({ name: 'user', params: { userId: 123 }}) // 带查询参数,变成 /register?plan=private router.push({ path: 'register', query: { plan: 'private' }}) 7.7. 嵌套路由

    嵌套路由跟普通路由基本没有什么区别。但是可以让vue开发变的非常灵活。 官网这块写的也非常好,我就直接拷贝了(原谅我吧。) 实际生活中的应用界面,通常由多层嵌套的组件组合而成。同样地,URL 中各段动态路径也按某种结构对应嵌套的各层组件,例如:

    /user/foo/profile /user/foo/posts +------------------+ +-----------------+ | User | | User | | +--------------+ | | +-------------+ | | | Profile | | +------------> | | Posts | | | | | | | | | | | +--------------+ | | +-------------+ | +------------------+ +-----------------+ 借助 vue-router,使用嵌套路由配置,就可以很简单地表达这种关系。 <div id="app"> <router-view></router-view> </div> const User = { template: '<div>User {{ $route.params.id }}</div>' } const router = new VueRouter({ routes: [ { path: '/user/:id', component: User } ] }) 这里的 <router-view> 是最顶层的出口,渲染最高级路由匹配到的组件。同样地,一个被渲染组件同样可以包含自己的嵌套 <router-view>。例如,在 User 组件的模板添加一个 <router-view>: const User = { template: ` <div class="user"> <h2>User {{ $route.params.id }}</h2> <router-view></router-view> </div> ` } 要在嵌套的出口中渲染组件,需要在 VueRouter 的参数中使用 children 配置: const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ { // 当 /user/:id/profile 匹配成功, // UserProfile 会被渲染在 User 的 <router-view> 中 path: 'profile', component: UserProfile }, { // 当 /user/:id/posts 匹配成功 // UserPosts 会被渲染在 User 的 <router-view> 中 path: 'posts', component: UserPosts } ] } ] })

    要注意,以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。 你会发现,children 配置就是像 routes 配置一样的路由配置数组,所以呢,你可以嵌套多层路由。

    此时,基于上面的配置,当你访问 /user/foo 时,User 的出口是不会渲染任何东西,这是因为没有匹配到合适的子路由。如果你想要渲染点什么,可以提供一个 空的 子路由:

    const router = new VueRouter({ routes: [ { path: '/user/:id', component: User, children: [ // 当 /user/:id 匹配成功, // UserHome 会被渲染在 User 的 <router-view> 中 { path: '', component: UserHome }, // ...其他子路由 ] } ] })

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 54 0 1 发布

    组件其实就是一个拥有样式、动画、js逻辑、HTML结构的综合块。前端组件化确实让大的前端团队更高效的开发前端项目。而作为前端比较流行的框架之一,Vue的组件和也做的非常彻底,而且有自己的特色。尤其是她单文件组件开发的方式更是非常方便,而且第三方工具支持也非常丰富,社区也非常活跃,第三方组件也呈井喷之势。当然学习和使用Vue的组件也是我们的最重要的目标。

    6.1. 全局扩展方法Vue.extend

    Vue提供了一个全局的API,Vue.extend可以帮助我们对Vue实例进行扩展,扩展完了之后,就可以用此扩展对象创建新的Vue实例了。 类似于继承的方式。

    语法:Vue.extend( options ) 参数: {Object} options 用法: 使用基础 Vue 构造器,创建一个“子类”。参数是一个包含组件选项的对象[后面会细讲]。 data 选项是特例,需要注意 - 在 Vue.extend() 中它必须是函数

    下面是一个官网demo:

    <div id="mount-point"></div> <script> // 创建构造器 var Profile = Vue.extend({ // 新的对象的模板,所有子实例都会拥有此模板 template: '<p>{{firstName}} {{lastName}} aka {{alias}}</p>', data: function () { // 创建的Vue实例时,data可以是Object 也可以是Function,但是在扩展 return { // 的时候,data必须是一个函数,而且要返回值奥。 firstName: 'Walter', lastName: 'White', alias: 'Heisenberg' } } }) </script> // 创建 Profile 实例,并挂载到一个元素上。 new Profile().$mount('#mount-point') // .$mount() 方法跟设置 el属性效果是一致的。

    结果如下:

    <p>Walter White aka Heisenberg</p>

    综合案例代码:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之extend全局方法</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> </div> <script> var myVue = Vue.extend({ template: '<div>{{ name }} - {{ age }} - {{ mail }}</div>', data: function () { return { name: 'malun', age: '19', mail: 'flydragonml@gmail.com' }; } }); var app = new myVue({ el: '#app' }); </script> </body> </html> 6.2. 创建组件和注册组件

    当然上面的方式只是能让我们继承Vue实例做一些扩展的动作。看Vue中如何创建一个组件并注册使用。

    Vue提供了一个全局注册组件的方法:Vue.component。

    语法: Vue.component( id, [definition] ) 参数: {string} id 组件的名字,可以当HTML标签用,注意组件的名字都是小写,而且最好有横线和字母组合。 {Function | Object} [definition] 组件的设置 用法: 注册或获取全局组件。注册还会自动使用给定的id设置组件的名称 // 注册组件,传入一个扩展过的构造器 Vue.component('my-component', Vue.extend({ /* ... */ })) // 注册组件,传入一个选项对象(自动调用 Vue.extend) Vue.component('my-component', { /* ... */ }) // 获取注册的组件(始终返回构造器) var MyComponent = Vue.component('my-component')

    简单demo:

    <div id="example"> <!--组件直接跟普通的标签一样的使用。--> <my-component></my-component> </div> // 注册一个组件 Vue.component('my-component', { // 模板选项设置当前组件,最终输出的html模板。注意:有且只有一个根元素。 template: '<div>A custom component!</div>' }) // 创建根实例 new Vue({ el: '#example' })

    那么我们注册一个组件自动帮我生成 label和radiobutton组合。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之extend全局方法</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!--组件名直接可以当标签使用。--> <radio-tag rid="rBas" txt="篮球" val="1"></radio-tag> <!--组件的属性也可以使用Vue的绑定的语法,下面是动态绑定数据给子组件--> <radio-tag :rid="demoId" :txt="demoText" :val="demoVal"></radio-tag> </div> <script> // 定义组件模板,模板必须有且只有一个根元素。 var temp = '<div><label v-bind:for="rid">{{ txt }}</label><input :id="rid" type="radio" v-bind:value="val"></div>'; // 注册一个全局的组件 Vue.component('radio-tag', { // 组件的名字不能有大写字母,跟React的曲别啊。另外组件名最好是小写字母加横线组合。 template: temp, props: ['rid', 'txt', 'val'], // 设置组件的属性有哪些,定义标签的属性一致。 data: function () { // 注意属性名都得是小写,不然会不认的。 return { // 在组件的定义中data必须是函数,而且必须有返回值。 age: 19, // 此地方的 age 和 emial都是演示,并么有有到。 email: 'flydragonml@gmail.com' } } }); // 初始化一个Vue实例 var app = new Vue({ el: '#app', data: { demoId: 'ft', demoText: '足球', demoVal: 2 } }); </script> </body> </html>组件的名字都必须是小写【其实是非必须,但是为了不麻烦就强制吧】!!!而且建议是小写字母和横线的组合比如: my-radiobtn注册组件的时候,可以传入一个选项对象进行配置。其中props是设置当前组件的属性,属性也都必须小写。属性是连接父容器和子组件的桥梁。注意:属性名和组件的名字都要小写啊,不然vue不会认的。编写组件代码最好配合Vue的chrome插件:vue-devtool组件可以返还自己的数据,但是必须是函数。data必须是Function6.3. 局部注册组件

    全局注册组件就是使用全局API

    Vue.componet(id, {....})就行了,当然我们有时候需要注册一个局部模块的自己用的组件。那么就可以用下面的方式了。

    var Child = { template: '<div>A custom component!</div>' } new Vue({ // ... components: { // <my-component> 将只在父模板可用 'my-component': Child } }) 6.4. 组件的slot

    使用组件的时候,经常需要在父组件中为子组件中插入一些标签等。当然其实可以通过属性等操作,但是比较麻烦,直接写标签还是方便很多。 那么Vue提供了slot协助子组件对父容器写入的标签进行管理。

    当父容器写了额外的内容时, 如果子组件恰好有一个slot标签,那边子容器的slot标签会被父容器写入的内容替换掉。

    比如下面的例子:

    <!DOCTYPE html> <<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之extend全局方法</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!--父容器输入标签--> <my-slot> <h3>这里是父容器写入的</h3> </my-slot> <!--父容器绑定数据到子容器的slot,这里的作用域是父容器的啊。--> <my-slot>{{ email }}</my-slot> <!--父容器什么都不传内容--> <my-slot></my-slot> </div> <script> // 反引号:可以定义多行字符串。 var temp = ` <div> <h1>这里是子组件</h1> <hr> <slot>slot标签会被父容器写的额外的内容替换掉,如果父容器没有写入任何东西,此标签将保留!</slot> </div> `; Vue.component('MySlot', { // 如果定义的组件为MySlot,那么用组件的时候:<my-slot></my-slot> template: temp, }); // 初始化一个Vue实例 var app = new Vue({ el: '#app', data: { email: 'flydragon@gmail.com' } }); </script> </body> </html>

    最终结果:

    <div id="app"> <div> <h1>这里是子组件</h1> <hr> <h3>这里是父容器写入的</h3> </div> <div> <h1>这里是子组件</h1> <hr> flydragon@gmail.com </div> <div> <h1>这里是子组件</h1> <hr> slot标签会被父容器写的额外的内容替换掉,如果父容器没有写入任何东西,此标签将删除! </div> </div> 6.5. 单文件组件的使用方式介绍

    通过上面我们定义组件的方式,就已经感觉很不爽了,尤其是模板的定义,而且样式怎么处理也没有很好的进行规整。 Vue可以通过Webpack等第三方工具实现单文件的开发的方式。当然这里会牵扯到很多es6的语法、第三方工具实现前端模块化等很多知识, 我们大概看一眼指导Vue的组件可以直接写一个文件中,其他地方就可以直接导入这个模块了。后面做项目的时候我还会再讲一下怎么用。

    <template> <div> <nav class="navbar navbar-dark navbar-fixed-top"> </nav> <div class="col-md-3 sidebar"> <ul> <li v-for="item in list" > <router-link :to="{ path: item.url }">{{ item.name }}</router-link> </li> </ul> </div> <div class="container-fluid content"> <router-view></router-view> </div> </div> </div> </template> <script> // 这里怎么回事 import Axios from 'axios' export default { name: 'app', components: { }, data: function () { return { list: [] } }, mounted: function () { // 挂在完成后 this.$nextTick(function () { Axios.get('/api/menulist', { params: { } }).then(function (res) { this.list = res.data }.bind(this)) }) } } </script> <style> ul, li { list-style: none; } .router-link-active { background-color: #f6f6f6; } .navbar { height: 50px; background-color: #303030; } .content { margin-top: 50px; padding-left: 210px; } .sidebar { background-color: #f5f5f5; border-right: 1px solid #eee; width: 200px; } @media (min-width: 768px) { .sidebar { position: fixed; top: 51px; bottom: 0; left: 0; z-index: 1000; display: block; padding: 20px; overflow-x: hidden; overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */ background-color: #f5f5f5; border-right: 1px solid #eee; } } </style>6.6. 组件总结

    Vue的组件化还是做的比较彻底的。不像Angular1.0中的模块那么鸡肋。组件化确实让前端模块化开发更加容易实现, Vue的单文件开发组件的方式也是Vue的一大创新,也发非常好用。

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 64 0 1 发布

    Vue的实例是Vue框架的入口,其实也就是前端的ViewModel,它包含了页面中的业务逻辑处理、数据模型等,当然它也有自己的一系列的生命周期的事件钩子,辅助我们进行对整个Vue实例生成、编译、挂着、销毁等过程进行js控制。

    5.1. Vue实例初始化的选项配置对象详解

    前面我们已经用了很多次 new Vue({...})的代码,而且Vue初始化的选项都已经用了data、methods、el、computedd等,估计您看到这里时,应该已经都明白了他们的作用,我们就详细讲解一下他们的使用情况。更详细的请参考官网内容

    5.1.1. Vue实例的的data对象介绍

    Vue的实例的数据对象data 我们已经用了很多了,数据绑定离不开data里面的数据。也是Vue的核心属性。 它是Vue绑定数据到HTML标签的数据源泉,另外Vue框架会自动监视data里面的数据变化,自动更新数据到HTML标签上去。本质原理是:Vue会自动将data里面的数据进行递归抓换成getter和setter,然后就可以自动更新HTML标签了,当然用getter和setter所以老的浏览器Vue支持的不够好。

    data对象的类型:

    类型是Object或者Function。如果是组件对象中,data必须是Function类型。【后面学了组件后就明白了,暂时对组件先放放。】

    实例:

    // 创建普通的Vue实例 var vm = new Vue({ data: data }) // 组件定义【后面会详细讲的】 // Vue.extend() 中 data 必须是函数 var Component = Vue.extend({ data: function () { //这里必须是函数!!!! return { a: 1 } } }) 5.1.2. Vue实例的computed介绍

    Vue的计算属性(computed)的属性会自动混入Vue的实例中。所有 getter 和 setter 的 this 上下文自动地绑定为 Vue 实例。这就很强大了,再计算属性中定义的函数里面可以直接使用指向了vue实例的this,异常方便的啊。

    类型

    { 键:函数} { [key: string]: Function | { get: Function, set: Function } } 当然,可以省略setter,如果省略了setter,那么值就可以是普通函数,但是必须有返回值。

    官方的实例 var vm = new Vue({ data: { a: 1 }, computed: { // 仅读取,值只须为函数 aDouble: function () { return this.a * 2 }, // 读取和设置 aPlus: { get: function () { return this.a + 1 }, set: function (v) { this.a = v - 1 } } } }) vm.aPlus // -> 2 vm.aPlus = 3 vm.a // -> 2 vm.aDouble // -> 4 5.1.3. methods

    类型: { [key: string]: Function }

    详细:

    methods 将被混入到 Vue 实例中。可以直接通过 VM 实例访问这些方法,或者在指令表达式中使用。方法中的 this 自动绑定为 Vue 实例。

    注意,不应该使用箭头函数来定义 method 函数 (例如 plus: () => this.a++)。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.a 将是 undefined。

    示例: var vm = new Vue({ data: { a: 1 }, methods: { plus: function () { this.a++ } } }) vm.plus() vm.a // 2 5.1.4. watch类型

    { [key: string]: string | Function | Object }

    详细:

    一个对象,键是需要观察的表达式,值是对应回调函数。值也可以是方法名,或者包含选项的对象。Vue 实例将会在实例化时调用 $watch(),遍历 watch 对象的每一个属性。

    示例: var vm = new Vue({ data: { a: 1, b: 2, c: 3 }, watch: { // 监控a变量变化的时候,自动执行此函数 a: function (val, oldVal) { console.log('new: %s, old: %s', val, oldVal) }, // 深度 watcher c: { handler: function (val, oldVal) { /* ... */ }, deep: true } } }) vm.a = 2 // -> new: 2, old: 1 //注意,不应该使用箭头函数来定义 watcher 函数 (例如 searchQuery: newValue => this.updateAutocomplete(newValue))。理由是箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,this.updateAutocomplete 将是 undefined。

    参考综合案例:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之数据监控</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>{{ number }}</p> <input type="button" name="btnGetNumber" value="增加" v-on:click="getNumber()"> </div> <script> var app = new Vue({ el: '#app', data: { number: 1 }, methods: { // 事件响应方法的逻辑代码 getNumber: function (e) { this.number += 1; // 不管是内联方法调用,还是绑定事件处理器两种方式执行事件响应方法的时候 this都是指向 app } }, watch: { // 监控number的变化,并自动执行下面的函数 number: function (val, oldVal) { console.log('val:' + val + ' - oldVal: ' + oldVal); } } }); </script> </body> </html? 5.1.5. 设置el的详解类型

    string | HTMLElement

    限制: 只在由 new 创建的实例中遵守。

    详细:

    提供一个在页面上已存在的 DOM 元素作为 Vue 实例的挂载目标,也就是说Vue绑定数据到哪里去找。可以是CSS 选择器,也可以是一个 HTMLElement实例。

    在实例挂载之后(生命周期的内容后面会详细讲的奥), 元素可以用 vm.$el 访问。

    如果这个选项在实例化时有作用,实例将立即进入编译过程,否则,需要显式调用 vm.$mount() 手动开启编译。

    var app = new Vue({ el: '#app', ... }); 5.2. Vue实例的生命周期

    Vue实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模板、挂载Dom、渲染→更新→渲染、卸载等一系列过程,我们称这是Vue的生命周期。通俗说就是Vue实例从创建到销毁的过程,就是生命周期。

    在Vue的整个生命周期中,它提供了一系列的事件,可以让我们注册js方法,可以让我们达到控制整个过程的目的地,哇赛,如果你搞过Asp.Net WebForm的话,你会发现整个就是WebForm的翻版嘛哈哈。值得注意的是,在这些事件响应方法中的this直接指向的是vue的实例。

    首先看看下面官网的一张生命周期的图,我做一下标注,看看整体的流程,后面我们上代码做一下效果。

    Vue提供的可以注册的钩子都在上图片的红色框标注。 他们是:

    beforeCreate

    在实例初始化之后,数据观测(data observer) 和 event/watcher 事件配置之前被调用。

    created

    实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算, watch/event 事件回调。然而,挂载阶段还没开始,$el 属性目前不可见。

    beforeMount

    在挂载开始之前被调用:相关的 render 函数首次被调用。

    mounted

    el 被新创建的 vm.$el 替换,并挂载到实例上去之后调用该钩子。如果 root 实例挂载了一个文档内元素,当 mounted 被调用时 vm.$el 也在文档内。

    beforeUpdate

    数据更新时调用,发生在虚拟 DOM 重新渲染和打补丁之前。 你可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程。

    updated

    由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用该钩子。

    当这个钩子被调用时,组件 DOM 已经更新,所以你现在可以执行依赖于 DOM 的操作。然而在大多数情况下,你应该避免在此期间更改状态,因为这可能会导致更新无限循环。

    该钩子在服务器端渲染期间不被调用。

    beforeDestroy

    实例销毁之前调用。在这一步,实例仍然完全可用。

    destroyed

    Vue 实例销毁后调用。调用后,Vue 实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。

    接下来我们做一个例子,看一下Vue中所有的生命周期怎么用的。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之生命周期</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>{{ number }}</p> <input type="text" name="btnSetNumber" v-model="number"> </div> <script> var app = new Vue({ el: '#app', data: { number: 1 }, beforeCreate: function () { console.log('beforeCreate 钩子执行...'); console.log(this.number) }, cteated: function () { console.log('cteated 钩子执行...'); console.log(this.number) }, beforeMount: function () { console.log('beforeMount 钩子执行...'); console.log(this.number) }, mounted: function () { console.log('mounted 钩子执行...'); console.log(this.number) }, beforeUpdate: function () { console.log('beforeUpdate 钩子执行...'); console.log(this.number) }, updated: function () { console.log('updated 钩子执行...'); console.log(this.number) }, beforeDestroy: function () { console.log('beforeDestroy 钩子执行...'); console.log(this.number) }, destroyed: function () { console.log('destroyed 钩子执行...'); console.log(this.number) }, }); </script> </body> </html>

    再看一个综合的实战的例子,可能涉及到ajax和组件,不过先看一下vue的生命周期的例子的用法:

    import Axios from 'axios' // 这是一个轻量级的ajax库,import是es6模块导入的语法。 export default { // 这是一个vue的模块,后面讲奥。 name: 'app', components: { }, data: function () { return { list: [] } }, mounted: function () { // 挂在完成后的生命周期钩子注册。 this.$nextTick(function () { // 等待下一次更新完成后执行业务处理代码。 Axios.get('/api/menulist', {// 将回调延迟到下次 DOM 更新循环之后执行。在修改数据之后立即使用它,然后等待 DOM 更新 params: { } }).then(function (res) { this.list = res.data }.bind(this)) }) } } 5.3. Vue实例的全局配置

    这一块都是一些小的知识点,我就不赘述了,直接copy 官网 Vue.config 是一个对象,包含 Vue 的全局配置。可以在启动应用之前修改下列属性:

    silent

    类型: boolean

    默认值: false

    用法:

    Vue.config.silent = true //取消 Vue 所有的日志与警告。 optionMergeStrategies

    optionMergeStrategies

    类型: { [key: string]: Function }

    默认值: {}

    用法:

    Vue.config.optionMergeStrategies._my_option = function (parent, child, vm) { return child + 1 } const Profile = Vue.extend({ _my_option: 1 })

    devtools

    类型: boolean

    默认值: true (生产版为 false)

    用法:

    // 务必在加载 Vue 之后,立即同步设置以下内容 Vue.config.devtools = true //配置是否允许 vue-devtools 检查代码。开发版本默认为 true,生产版本默认为 false。生产版本设为 true 可以启用检查。 er

    errorHandler

    类型: Function

    默认值: 默认抛出错误

    用法:

    Vue.config.errorHandler = function (err, vm) { // handle error } //指定组件的渲染和观察期间未捕获错误的处理函数。这个处理函数被调用时,可获取错误信息和 Vue 实例。 //Sentry, an error tracking service, provides official integration using this option.

    ignoredElements

    类型: Array

    默认值: []

    用法:

    Vue.config.ignoredElements = [ 'my-custom-web-component', 'another-web-component' ] 须使 Vue 忽略在 Vue 之外的自定义元素 (e.g., 使用了 Web Components APIs)。否则,它会假设你忘记注册全局组件或者拼错了组件名称,从而抛出一个关于 Unknown custom element 的警告。

    keyCodes

    类型: { [key: string]: number | Array }

    默认值: {}

    用法:

    Vue.config.keyCodes = { v: 86, f1: 112, mediaPlayPause: 179, up: [38, 87] } //给 v-on 自定义键位别名。 5.4. Vue的全局API

    Vue的全局API提供大量的功能,我这里就给大家罗列几个常用的结果,其他的还是参考官网.

    5.4.1. Vue.nextTick

    语法: Vue.nextTick( [callback, context] )

    参数:    {Function} [callback]{Object} [context]

    用法: 在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

    // 修改数据 vm.msg = 'Hello' // DOM 还没有更新 Vue.nextTick(function () { // DOM 更新了 }) 5.4.2. Vue.set

    语法: Vue.set( object, key, value )

    参数:{Object} object,{string} key,{any} value

    返回值: 设置的值.

    用法:
    设置对象的属性。如果对象是响应式的,确保属性被创建后也是响应式的,同时触发视图更新。这个方法主要用于避开 Vue 不能检测属性被添加的限制。 注意对象不能是 Vue 实例,或者 Vue 实例的根数据对象。

    5.4.3. Vue.compile语法:

    Vue.compile( template )

    参数:

    {string} template

    用法: //在render函数中编译模板字符串。只在独立构建时有效 var res = Vue.compile('<div><span>{{ msg }}</span></div>') new Vue({ data: { msg: 'hello' }, render: res.render, staticRenderFns: res.staticRenderFns }) 5.4.4. 全局API总结

    其实还有几个其他的全局API,不打算在这里讲了,比如扩展组件Vue.extend 的用法、Vue.use加载插件、Vue.filter加载过滤器、Vue.directive自定义指令等 后面再讲其他Vue的知识点的时候,再加上这些,全局API其实就是Vue类型的静态方法,全局范围内都可以使用的,某些实例的方法本质也是调用了这些全局的,后面用到时候再说。

    5.5. Vue实例与生命周期总结

    Vue的实例封装的还是挺有艺术性的,很符合开发者的思维规范,它的生命周期也非常清晰,使用起来也非常方便。Vue确实一个好框架。

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 75 0 1 发布

    4.1. 监听事件的Vue处理

    Vue提供了协助我们为标签绑定时间的方法,当然我们可以直接用dom原生的方式去绑定事件。Vue提供的指令进行绑定也是非常方便,而且能让ViewModel更简洁,逻辑更彻底。所以还是推荐大家使用的。

    Vue提供了v-on指令帮助我们进行事件的绑定。 基本的内联事件处理方法[官方demo]:

    <div id="example-1"> <!-- 为按钮绑定点击事件,执行counter += 1的任务。 --> <button v-on:click="counter += 1">增加 1</button> <p>这个按钮被点击了 {{ counter }} 次。</p> </div> <script> var example1 = new Vue({ el: '#example-1', data: { counter: 0 } }) </script> 4.2. 事件处理方法集成到Vue对象

    内联的方式绑定的事件,只能处理简单的事件的处理逻辑。复杂的情况还是封装到js中最方便,也不容易出错。 Vue对象中可以添加methods属性,开发者可以把事件处理函数的逻辑放到methods中。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之动态显示表格</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <p>{{ number }}</p> <input type="button" name="btnGetNumber" value="增加[绑定事件处理器]" v-on:click="getNumber"> <input type="button" name="btnGetNumber" value="增加[内联方法调用]" v-on:click="getNumber()"> </div> <script> var app = new Vue({ el: '#app', data: { number: 1 }, methods: { // 事件响应方法的逻辑代码 getNumber: function (e) { this.number += 1; // 不管是内联方法调用,还是绑定事件处理器两种方式执行事件响应方法的时候 this都是指向 app } } }); </script> </body> </html> 4.3. 事件修饰符

    官网上写的非常好,这块就直接用管网的吧。在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在 methods 中轻松实现这点,但更好的方式是:methods 只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。 为了解决这个问题, Vue.js 为 v-on 提供了 事件修饰符。通过由点(.)表示的指令后缀来调用修饰符。

    .stop.prevent.capture.self.once <!-- 阻止单击事件冒泡 --> <a v-on:click.stop="doThis"></a> <!-- 提交事件不再重载页面 --> <form v-on:submit.prevent="onSubmit"></form> <!-- 修饰符可以串联 --> <a v-on:click.stop.prevent="doThat"></a> <!-- 只有修饰符 --> <form v-on:submit.prevent></form> <!-- 添加事件侦听器时使用事件捕获模式 --> <div v-on:click.capture="doThis">...</div> <!-- 只当事件在该元素本身(而不是子元素)触发时触发回调 --> <div v-on:click.self="doThat">...</div> <!-- the click event will be triggered at most once --> <a v-on:click.once="doThis"></a>
    4.4. 按键修饰符

    在监听键盘事件时,我们经常需要监测常见的键值。 Vue 允许为 v-on 在监听键盘事件时添加按键修饰符:

    <!-- 只有在 keyCode 是 13 时调用 vm.submit() --> <input v-on:keyup.13="submit"> 记住所有的 keyCode 比较困难,所以 Vue 为最常用的按键提供了别名: <!-- 同上 --> <input v-on:keyup.enter="submit"> <!-- 缩写语法 --> <input @keyup.enter="submit">

    全部的按键别名:

    .enter.tab.delete (捕获 “删除” 和 “退格” 键).esc.space.up.down.left.right.ctrl.alt.shift.meta4.5. 事件绑定的简写

    Vue中属性的绑定的简写直接是: === 'v-bind:'
    而事件的缩写是直接变成@. 也就是说: v-on: === @ 看下面的例子: 

    <!-- 完整语法 --> <a v-on:click="doSomething"></a> <!-- 缩写 --> <a @click="doSomething"></a> 4.6. 事件绑定总结

    Vue为了方便大家进行开发,提供了事件的相关的封装,可以让我们方便我们用Vue对事件进行开发,尤其是v-on指令的非常方便的跟Vue对象中methods进行配合进行复杂的事件处理,非常方便。另外事件的事件修饰符和按键修饰符也可以让Vue事件这块锦上添花。

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 57 0 1 发布

    3.1. 条件渲染

    有时候我们要根据数据的情况,决定标签是否进行显示或者有其他动作。最常见的就是,表格渲染的时候,如果表格没有数据,就显示无数据。如果有数据就显示表格数据。 Vue帮我们提供了一个v-if的指令,帮助我们完成判断的模板处理。

    <div id="app"> <h1 v-if="ok">Yes</h1> <h1 v-else>No</h1> </div> <!-- 当ok为true的时候,输出: Yes, 否则输出: No --> <script> var app = new Vue({ el: '#app', data: { ok: true // true,返回:Yes, false=> No } }); </script>

    v-if指令可以根据数据绑定的情况进行插入标签或者移除标签。 当然,如果熟悉js的都清楚,有if,肯定会有else。 Vue提供的是 v-else指令。

    3.2. 列表渲染3.2.1. 基本v-for循环渲染标签

    模板引擎都会提供循环的支持。Vue也不例外,Vue是提供了一个v-for指令。基本的用法类似于foreach的用法。还是看例子最直接,上代码:

    <div id="app"> <table> <thead> <tr> <th>姓名</th> <th>年龄</th> <th>地址</th> </tr> </thead> <tbody> <!-- 每次for循环,都会创建一个tr标签。item是遍历的元素。 --> <tr v-for="item in UserList" > <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td>{{ item.address }}</td> </tr> </tbody> </table> </div> <script> var app = new Vue({ el: '#app', data: { UserList: [ {'name': 'malun', 'age': 18, 'address': '北京黑地下室'}, {'name': 'flydragon', 'age': 22, 'address': '厦门的很多热的地方'}, {'name': 'temp', 'age': 25, 'address': '东北松花江上'} ] } }); </script> 3.2.2. Template循环渲染多标签

    上面的例子,我们演示的是 每次循环输出一个tr标签。如果我们希望每次循环生成两个tr标签呢?如果还有生成其他的标签呢?

    Vue给我们提供了template标签,供我们用于v-for循环中进行处理。

    上代码喽:

    <ul> <!-- 通过template标签,可以一次循环,输出两个li标签 --> <template v-for="item in items"> <li>{{ item.msg }}</li> <li class="divider"></li> </template> </ul> 3.2.3. 关于v-for对应的数组的更新

    由于Vue的机制就是检测数据的变化,自动跟新HTML。数组的变化,Vue之检测部分函数,检测的函数执行时才会触发视图更新。这些方法如下:

    push()pop()shift()unshift()splice()sort()reverse()3.3. 表格显示的综合案例

    下面是一个综合的案例,每秒钟往表格中添加一条数据。 本案例综合使用了v-if 和 v-for循环综合案例。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之动态显示表格</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <table> <thead> <tr> <th>姓名</th> <th>年龄</th> <th>地址</th> </tr> </thead> <!-- 如果列表有数据,直接输出表格数据,没有数据提示用户没有数据 --> <tbody v-if="UserList.length > 0"> <tr v-for="item in UserList" > <td>{{ item.name }}</td> <td>{{ item.age }}</td> <td>{{ item.address }}</td> </tr> </tbody> <tbody v-else> <tr><td colspan="3">没有数据奥!</td></tr> </tbody> </table> </div> <script> var app = new Vue({ el: '#app', data: { UserList: [] } }); // 每秒钟插入一条数据。 setInterval(function () { app.UserList.push({'name': 'malun', 'age': 18, 'address': '北京黑地下室'}); }, 1000); </script> </body> </html> 3.4. 总结列表和条件绑定

    列表的使用其实本质还是js的衍生使用,对于有js开发基础的没有什么难度。关键是多写几个案例就会详细通了。

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 70 0 1 发布
    2.1. 什么是双向绑定?

    Vue框架很核心的功能就是双向的数据绑定。 双向是指:HTML标签数据 绑定到 Vue对象,另外反方向数据也是绑定的。通俗点说就是,Vue对象的改变会直接影响到HTML的标签的变化,而且标签的变化也会反过来影响Vue对象的属性的变化。
    这样以来,就彻底变革了之前Dom的开发方式,之前Dom驱动的开发方式尤其是以jQuery为主的开发时代,都是dom变化后,触发js事件,然后在事件中通过js代码取得标签的变化,再跟后台进行交互,然后根据后台返回的结果再更新HTML标签,异常的繁琐。有了Vue这种双向绑定,让开发人员只需要关心json数据的变化即可,Vue自动映射到HTML上,而且HTML的变化也会映射回js对象上,开发方式直接变革成了前端由数据驱动的 开发时代,远远抛弃了Dom开发主导的时代了。

    2.2. Vue绑定文本

    数据绑定最常见的形式就是使用 “Mustache” 语法(双大括号)的文本插值,比如模板引擎:handlebars中就是用的{{}}.
    创建的Vue对象中的data属性就是用来绑定数据到HTML的。参考如下代码:

    <span>Message: {{ msg }}</span> <script> var app = new Vue({ // 创建Vue对象。Vue的核心对象。 el: '#app', // el属性:把当前Vue对象挂载到 div标签上,#app是id选择器 data: { // data: 是Vue对象中绑定的数据 msg: 'Hello Vue!' // message 自定义的数据 } }); </script> 2.3. 绑定数据中使用JavaScript表达式

    对于所有的数据绑定, Vue.js 都提供了完全的 JavaScript 表达式支持。

    <span>Message: {{ msg + ' - ' + name }}</span> <script> var app = new Vue({ // 创建Vue对象。Vue的核心对象。 el: '#app', // el属性:把当前Vue对象挂载到 div标签上,#app是id选择器 data: { // data: 是Vue对象中绑定的数据 msg: 'Hi', // message 自定义的数据 name: 'flydragon' // name自定义的属性,vue可以多个自定义属性,属性类型也可是复杂类型 } }); </script>

    结果:

    Hi - flydragon

    当然Vue还可以支持表达中的任何计算、函数处理等。参考下面的综合点的案例。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之数据绑定-表达式运算</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> {{ msg + ' - ' + name }} <p> {{ isOk ? '123' : '456' }} </p> <p>我的年龄是: {{ age *2 }}</p> </div> <script> var app = new Vue({ // 创建Vue对象。Vue的核心对象。 el: '#app', // el属性:把当前Vue对象挂载到 div标签上,#app是id选择器 data: { // data: 是Vue对象中绑定的数据 msg: 'Hi', // message 自定义的数据 name: 'flydragon', isOk: true, age: 18 } }); </script> </body> </html> 2.4. Vue属性绑定

    Vue中不能直接使用{{ expression }} 语法进行绑定html的标签,而是用它特有的v-bind指令(就是一种写法,先按照格式走,具体指令是什么可以后续再了解)。

    绑定的语法结构:

    <span v-bind:id="menuId">{{ menuName }}</span>

    参考如下代码案例:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之数据绑定--属性绑定</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div v-bind:id="MenuContaineId"> <a href="#" v-bind:class="MenuClass">首页</a> <a href="#" v-bind:class="MenuClass">产品</a> <a href="#" v-bind:class="MenuClass">服务</a> <a href="#" v-bind:class="MenuClass">关于</a> </div> </div> <script> var app = new Vue({ el: '#app', data: { // data: 是Vue对象中绑定的数据 MenuClass: 'top-menu', MenuContaineId: 'sitemenu' } }); </script> </body> </html> 2.5. 属性绑定简写

    由于v-bind 使用非常频繁,所以Vue提供了简单的写法,可以去掉v-bind直接使用:即可。例如:

    <div :id="MenuContaineId">

    等价于:

    <div v-bind:id="MenuContaineId"> 2.6. 输出纯HTML

    由于Vue对于输出绑定的内容做了提前encode,保障在绑定到页面上显示的时候不至于被xss攻击。但某些场景下,我们确保后台数据是安全的,那么我们就要在网页中显示原生的HTML标签。Vue提供了v-html指令。

    <div id="app"> <div v-bind:id="MenuContaineId" v-html="MenuBody"> </div> </div> <script> var app = new Vue({ el: '#app', data: { // data: 是Vue对象中绑定的数据 MenuContaineId: 'menu', MenuBody: '<p>这里是菜单的内容</p>' } }); </script>

    结果:

    <div id="app"> <div id="menu"> <p>这里是菜单的内容</p> </div> </div> 2.7. 样式绑定

    对于普通的属性的绑定,只能用上面的讲的绑定属性的方式。而Vue专门加强了class和style的属性的绑定。可以有复杂的对象绑定、数组绑定样式和类。

    2.7.1. 绑定样式对象

    经常我们需要对样式进行切换,比如:div的显示和隐藏,某些标签active等。Vue提供的对象绑定样式的方式就很容做这些事情。

    代码: 

    <div v-bind:class="{ active: isActive }"></div> 解释:当 isActive为 true时, div就会具有了active样式类,如果 isActive为false,那么div就去掉active样式类。<span></span> <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之绑定样式类</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> <style> .active { background-color: #ccc; } </style> </head> <body> <div id="app"> <div v-bind:id="MenuContaineId" v-bind:class="{ active: isActive }"> 绑定颜色类 </div> </div> <script> var app = new Vue({ el: '#app', data: { // data: 是Vue对象中绑定的数据 MenuContaineId: 'menu', isActive: true } }); </script> </body> </html> 

    解释: 当 isActive为 true时, div就会具有了active样式类,如果 isActive为false,那么div就去掉active样式类。

    2.7.2. 混合普通的HTML标签样式类及绑定样式对象

    v-bind:class 指令可以与普通的 class 属性共存。

    <div id="app"> <div class="static" v-bind:class="{ active: isActive, 'text-danger': hasError }"> </div> </div> <script> var app = new Vue({ el: '#app', data: { // data: 是Vue对象中绑定的数据 isActive: true, hasError: false } }); </script>

    结果:

    <div id="app"> <div class="static active"> </div> </div> 2.7.3. 绑定data中的样式对象

    直接在html属性中的双引号内写对象,还是很不爽,也没有智能提示,很容易写错。 Vue可以让我们直接把绑定的class字符串指向data的一个对象,这样就非常方便了,既可以有智能提示,又可以很复杂进行编辑,不用担心烦人的""了。

    <div id="app"> <div class="static" v-bind:class="classObject"> </div> </div> <script> var app = new Vue({ el: '#app', data: { classObject: { active: true, 'text-danger': false } } }); </script>

    结果:

    <div id="app"> <div class="static active"> </div> </div> 2.7.4. 绑定样式数组

    其实绑定数组,就是绑定样式对象的延续,看官网的例子代码吧。

    <div v-bind:class="[activeClass, errorClass]"> data: { activeClass: 'active', errorClass: 'text-danger' }

    当然还有很多其他很有趣的支持,就不赘述了。例如:

    例如: <div v-bind:class="[isActive ? activeClass : '', errorClass]"> <div v-bind:class="[{ active: isActive }, errorClass]"> 2.7.5. 内联样式绑定

    内联样式的绑定,非常类似于样式类的操作。v-bind:style 的对象语法十分直观——看着非常像 CSS ,其实它是一个 JavaScript 对象。 CSS属性名可以用驼峰式(camelCase)或短横分隔命名(kebab-case)。

    看个例子:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之htmlraw</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <div v-bind:style="{fontSize: size + 'px', backgroundColor: bgcolor, width: width}"> vue 入门系列教程 </div> </div> <script> var app = new Vue({ el: '#app', data: { size: 19, width: 200, bgcolor: 'red' } }); </script> </body> </html>

    自动添加前缀
    当 v-bind:style 使用需要特定前缀的 CSS 属性时,如 transform ,Vue.js 会自动侦测并添加相应的前缀。

    2.8. 计算属性

    在做数据的绑定的时候,数据要进行处理之后才能展示到html页面上,虽然vue提供了非常好的表达式绑定的方法,但是只能应对低强度的需求。比如: 把一个日期按照规定格式进行输出,可能就需要我们对日期对象做一些格式化的出来,表达式可能就捉襟见肘了。

    Vue对象提供的computed属性,可以让我们开发者在里面可以放置一些方法,协助我们绑定数据操作,这些方法可以跟data中的属性一样用,注意这些方法用的时候不要加()。 例子来了:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之htmlraw</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <table> <tr> <!-- computed里面的函数可以直接当成data里面的属性用,非常方便,注意没有括号!!!--> <td>生日</td><td>{{ getBirthday }}</td> </tr> <tr> <td>年龄</td><td>{{ age }}</td> </tr> <tr> <td>地址</td><td>{{ address }}</td> </tr> </table> </div> <script> var app = new Vue({ el: '#app', data: { birthday: 914228510514, // 这是一个日期对象的值:1998年11月1日 age: 19, address: '北京昌平区龙泽飞龙' }, computed: { // 把日期换成 常见规格格式的字符串。 getBirthday: function () { var m = new Date(this.birthday); return m.getFullYear() + '年' + m.getMonth() +'月'+ m.getDay()+'日'; } } }); </script> </body> </html> 2.9. 绑定的数据过滤器

    过滤器本质就是数据在呈现之前先进行过滤和筛选。官网上写的不错,我就不再赘述,下面是官网的描述。

    Vue.js 允许你自定义过滤器,被用作一些常见的文本格式化。过滤器应该被添加在 mustache 插值的尾部,由“管道符”指示:

    {{ message | capitalize }} <!-- in mustaches --> {{ message | capitalize }} <!-- in v-bind --> <div v-bind:id="rawId | formatId"></div> Vue 2.x 中,过滤器只能在 mustache 绑定和 v-bind 表达式(从 2.1.0 开始支持)中使用,因为过滤器设计目的就是用于文本转换。为了在其他指令中实现更复杂的数据变换,你应该使用计算属性。 过滤器函数总接受表达式的值作为第一个参数。 new Vue({ // ... filters: { capitalize: function (value) { if (!value) return '' value = value.toString() return value.charAt(0).toUpperCase() + value.slice(1) } } }) 过滤器可以串联: {{ message | filterA | filterB }} 过滤器是 JavaScript 函数,因此可以接受参数: {{ message | filterA('arg1', arg2) }} 这里,字符串 'arg1' 将传给过滤器作为第二个参数, arg2 表达式的值将被求值然后传给过滤器作为第三个参数。 2.10. 核心:自动响应对象的变化到HTML标签

    上面的例子都是 数据对象是写死在创建的Vue对像上,那如果数据(data)发生改变时会怎样呢? 让我们用chrome把上面例子的页面打开,并打开发者工具控制台,输入:app.age = 20 会有什么情况发生呢?

     

    在页面中添加一个按钮,动态的增加年龄:

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之htmlraw</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <table> <tr> <!-- computed里面的函数可以直接当成data里面的属性用,非常方便,注意没有括号!!!--> <td>生日</td><td>{{ getBirthday }}</td> </tr> <tr> <td>年龄</td><td>{{ age }}</td> </tr> <tr> <td>地址</td><td>{{ address }}</td> </tr> </table> </div> <!-- 添加下面这行代码,动态增加 年龄,页面会有怎样的变化呢?? --> <button type="button" onclick="app.age+=1;" >加加</button> <script> var app = new Vue({ el: '#app', data: { birthday: 914228510514, // 这是一个日期对象的值:1998年11月1日 age: 19, address: '北京昌平区龙泽飞龙' }, computed: { // 把日期换成 常见规格格式的字符串。 getBirthday: function () { var m = new Date(this.birthday); return m.getFullYear() + '年' + m.getMonth() +'月'+ m.getDay()+'日'; } } }); </script> </body> </html> 2.11. 双向数据绑定

    上面的例子我们大多讲的是单向的 js对象向 HTML数据进行绑定,那HTML怎样向js进行反馈数据呢? HTML中只有表达能接受用户的输入,最简单的演示双向绑定的就是文本框了。

    Vue提供了一个新的指令:v-model进行双向数据的绑定,注意不是v-bind。

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之htmlraw</title> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <div id="app"> <!-- v-model可以直接指向data中的属性,双向绑定就建立了 --> <input type="text" name="txt" v-model="msg"> <p>您输入的信息是:{{ msg }}</p> </div> <script> var app = new Vue({ el: '#app', data: { msg: '双向数据绑定的例子' } }); </script> </body> </html>

    最终的结果就是:你改变input文本框的内容的时候,p标签中的内容会跟着进行改变,哇是不是很神奇呢...

    关于其他表单的绑定的语法我就不赘述了,还是参考官网吧,我这里大部分例子也是来自官网

    2.12. 数据绑定总结

    vue提供了大量的绑定的语法和方法,非常方便我们进行数据的绑定,尤其它是双向的数据绑定,极大的减少了我们dom操作的麻烦程度。可能你越来越喜欢它了吧...

    转载:https://www.cnblogs.com/fly_dr...

    作者:老马

  • Vue 70 0 1 发布
    1.1. Vue简介

    Vue是一个前端的双向绑定类的框架,发音[读音 /vjuː/, 类似于 view]。新的Vue版本参考了React的部分设计,当然也有自己独特的地方,比如Vue的单文件组件开发方式都很有创新,另外Vue自身的一些绑定的语法、用法等都非常精炼,很容易上手,而且第三方的插件都非常丰富,社区非常活跃,最新的文档都有中文版本。而且Vue配合官方的和第三方的库可以实现单文件的组件化开发、SPA等现代化前端开发。

    1.2. Vue的入门demo

    Vue 可以直接把它当做一个js库使用,所以它可以很容易的接入到你的项目或者单个页面中。甚至你可以只使用它的双向绑定功能。所以它很容易上手。

    比如:我们有一个需求,一个网页上一个Div标签,我们有一个json对象存储数据,把json对象上的数据放到Div上去。

    接下来是步骤:

    第一步: 创建一个文件夹并创建一个html文件 比如:index.html.
    当如你可以选择你自己的编辑器,我就用VSCode。第二步:引入Vue库
    当然了你可以直接下载Vue的js文件,推荐你直接用上面的cdn即可。第三步:创建一个Div,给它一个id,比如:app第四步:创建Vue的对象,并把数据绑定到上面创建好的div上去。

    最终的代码如下:

    <!DOCTYPE html> <!--第一步:创建文件夹及html文件--> <html lang="en"> <head> <meta charset="UTF-8"> <title>Vue入门之Helloworld</title> <!--第二步:引入Vue库--> <script src="https://unpkg.com/vue/dist/vue.js"></script> </head> <body> <!--第三步:创建一个Div--> <div id="app"> <!--Vue的模板的绑定数据的方法, 类似于很多其他前端的模板,可以用两对花括号进行绑定Vue中的数据对象的属性 --> {{ message }} </div> <!--第四步:创建Vue的对象,并把数据绑定到上面创建好的div上去。--> <script> var app = new Vue({ // 创建Vue对象。Vue的核心对象。 el: '#app', // el属性:把当前Vue对象挂载到 div标签上,#app是id选择器 data: { // data: 是Vue对象中绑定的数据 message: 'Hello Vue!' // message 自定义的数据 } }); </script> </body> </html> 

    最终的结果就是:

    Hello Vue!1.3. Vue的Helloworld总结Vue构造函数的:选项el属性,就是element缩写,当前Vue对象挂载到哪个标签上的语法,支持CSS选择器或者dom对象,一般用id选择器选择当前页面的标签。Vue的选项:data属性是自定义数据。这里我们只是演示了一个message属性,vue会把自定义的数据可以与html的模板数据进行绑定。Vue 数据绑定的方式就是用 {{}},类似于handlebars.上面这个demo就是演示了Vue的绑定数据的基本模型。注意点,标签先创建好了之后,再创建Vue对象,具体你应该懂吧。

    转载:https://www.cnblogs.com/fly_dragon/p/6218551.html

    作者:老马

  • 信息安全 148 0 1 发布

    概述

    HTTPS (Secure Hypertext Transfer Protocol)安全超文本传输协议,是一个安全通信通道,它基于HTTP开发用于在客户计算机和服务器之间交换信息。它使用安全套接字层(SSL)进行信息交换,简单来说它是HTTP的安全版,是使用TLS/SSL加密的HTTP协议。
    HTTP协议采用明文传输信息,存在信息窃听、信息篡改和信息劫持的风险,而协议TLS/SSL具有身份验证、信息加密和完整性校验的功能,可以避免此类问题发生。

    TLS/SSL全称安全传输层协议Transport Layer Security, 是介于TCP和HTTP之间的一层安全协议,不影响原有的TCP协议和HTTP协议,所以使用HTTPS基本上不需要对HTTP页面进行太多的改造。


    HTTPS和HTTP的区别是什么?


    什么是HTTPS

    HTTPS是在HTTP上建立SSL加密层,并对传输数据进行加密,是HTTP协议的安全版。HTTPS主要作用是:

    对数据进行加密,并建立一个信息安全通道,来保证传输过程中的数据安全;对网站服务器进行真实身份认证。什么是HTTP

     HTTP是互联网上应用最为广泛的一种网络协议,是一个客户端和服务器端请求和应答的标准(TCP),用于从WWW服务器传输超文本到本地浏览器的传输协议。HTTP是采用明文形式进行数据传输,极易被不法份子窃取和篡改。

    HTTPS和HTTP的区别   HTTPS是加密传输协议,HTTP是名文传输协议;   HTTPS需要用到SSL证书,而HTTP不用;   HTTPS比HTTP更加安全,对搜索引擎更友好,利于SEO【参考:(1)为保护用户隐私安全,谷歌优先索引HTTPS网页、(2)百度开放收录https站点,https全网化势不可挡】;   HTTPS标准端口443,HTTP标准端口80;   HTTPS基于传输层,HTTP基于应用层;   HTTPS在浏览器显示绿色安全锁,HTTP没有显示;

     总的来说HTTPS比HTTP更加安全,能够有效的保护网站用户的隐私信息安全,这也是为什么现在的HTTPS网站越来越多。如果不想你的网站因为数据泄露上头条的话,就赶快去申请一张SSL证书为自己的网站实现HTTPS加密吧!

    原文链接:https://blog.csdn.net/hherima/article/details/52469267

  • 信息安全 107 0 1 发布

    1、RSA身份验证的隐患

        身份验证和密钥协商是TLS的基础功能,要求的前提是合法的服务器掌握着对应的私钥。但RSA算法无法确保服务器身份的合法性,因为公钥并不包含服务器的信息,存在安全隐患:

        客户端C和服务器S进行通信,中间节点M截获了二者的通信;

        节点M自己计算产生一对公钥pub_M和私钥pri_M;

        C向S请求公钥时,M把自己的公钥pub_M发给了C;

        C使用公钥 pub_M加密的数据能够被M解密,因为M掌握对应的私钥pri_M,而 C无法根据公钥信息判断服务器的身份,从而 C和 M之间建立了"可信"加密连接;

        中间节点 M和服务器S之间再建立合法的连接,因此 C和 S之间通信被M完全掌握,M可以进行信息的窃听、篡改等操作。

        另外,服务器也可以对自己的发出的信息进行否认,不承认相关信息是自己发出。

        因此该方案下至少存在两类问题:中间人攻击和信息抵赖。

    2、身份验证CA和证书

        解决上述身份验证问题的关键是确保获取的公钥途径是合法的,能够验证服务器的身份信息,为此需要引入权威的第三方机构CA(如沃通CA)。CA 负责核实公钥的拥有者的信息,并颁发认证"证书",同时能够为使用者提供证书验证服务,即PKI体系(PKI基础知识)。

        基本的原理为,CA负责审核信息,然后对关键信息利用私钥进行"签名",公开对应的公钥,客户端可以利用公钥验证签名。CA也可以吊销已经签发的证书,基本的方式包括两类 CRL 文件和 OCSP。CA使用具体的流程如下:

        a.服务方S向第三方机构CA提交公钥、组织信息、个人信息(域名)等信息并申请认证;

        b.CA通过线上、线下等多种手段验证申请者提供信息的真实性,如组织是否存在、企业是否合法,是否拥有域名的所有权等;

        c.如信息审核通过,CA会向申请者签发认证文件-证书。

    证书包含以下信息:申请者公钥、申请者的组织信息和个人信息、签发机构 CA的信息、有效时间、证书序列号等信息的明文,同时包含一个签名;

    签名的产生算法:首先,使用散列函数计算公开的明文信息的信息摘要,然后,采用 CA的私钥对信息摘要进行加密,密文即签名;

        d.客户端 C 向服务器 S 发出请求时,S 返回证书文件;

        e.客户端 C读取证书中的相关的明文信息,采用相同的散列函数计算得到信息摘要,然后,利用对应 CA的公钥解密签名数据,对比证书的信息摘要,如果一致,则可以确认证书的合法性,即公钥合法;

        f.客户端然后验证证书相关的域名信息、有效时间等信息;

        g.客户端会内置信任CA的证书信息(包含公钥),如果CA不被信任,则找不到对应 CA的证书,证书也会被判定非法。

    在这个过程注意几点:

        a.申请证书不需要提供私钥,确保私钥永远只能服务器掌握;

        b.证书的合法性仍然依赖于非对称加密算法,证书主要是增加了服务器信息以及签名;

        c.内置 CA 对应的证书称为根证书,颁发者和使用者相同,自己为自己签名,即自签名证书(为什么说"部署自签SSL证书非常不安全")

        d.证书=公钥+申请者与颁发者信息+签名;

     ★即便有人截取服务器A证书,再发给客户端,想冒充服务器A,也无法实现。因为证书和url的域名是绑定的。

    3、证书链

        如 CA根证书和服务器证书中间增加一级证书机构,即中间证书,证书的产生和验证原理不变,只是增加一层验证,只要最后能够被任何信任的CA根证书验证合法即可。

        a.服务器证书 server.pem 的签发者为中间证书机构 inter,inter 根据证书 inter.pem 验证 server.pem 确实为自己签发的有效证书;

        b.中间证书 inter.pem 的签发 CA 为 root,root 根据证书 root.pem 验证 inter.pem 为自己签发的合法证书;

        c.客户端内置信任 CA 的 root.pem 证书,因此服务器证书 server.pem 的被信任。

    服务器证书、中间证书与根证书在一起组合成一条合法的证书链,证书链的验证是自下而上的信任传递的过程。

    二级证书结构存在的优势:

        a.减少根证书结构的管理工作量,可以更高效的进行证书的审核与签发;

        b.根证书一般内置在客户端中,私钥一般离线存储,一旦私钥泄露,则吊销过程非常困难,无法及时补救;

        c.中间证书结构的私钥泄露,则可以快速在线吊销,并重新为用户签发新的证书;

        d.证书链四级以内一般不会对 HTTPS 的性能造成明显影响。

    证书链有以下特点:

        a.同一本服务器证书可能存在多条合法的证书链。

        因为证书的生成和验证基础是公钥和私钥对,如果采用相同的公钥和私钥生成不同的中间证书,针对被签发者而言,该签发机构都是合法的 CA,不同的是中间证书的签发机构不同;

        b.不同证书链的层级不一定相同,可能二级、三级或四级证书链。

        中间证书的签发机构可能是根证书机构也可能是另一个中间证书机构,所以证书链层级不一定相同。

    4、证书吊销

        CA 机构能够签发证书,同样也存在机制宣布以往签发的证书无效。证书使用者不合法,CA 需要废弃该证书;或者私钥丢失,使用者申请让证书无效。主要存在两类机制:CRL 与 OCSP。

        a.CRL

        Certificate Revocation List, 证书吊销列表(什么是证书吊销列表(CRL)?吊销列表起什么作用),一个单独的文件。该文件包含了 CA 已经吊销的证书序列号(唯一)与吊销日期,同时该文件包含生效日期并通知下次更新该文件的时间,当然该文件必然包含 CA 私钥的签名以验证文件的合法性。

    证书中一般会包含一个 URL 地址 CRL Distribution Point,通知使用者去哪里下载对应的 CRL 以校验证书是否吊销。该吊销方式的优点是不需要频繁更新,但是不能及时吊销证书,因为 CRL 更新时间一般是几天,这期间可能已经造成了极大损失。

        b.OCSP

    Online Certificate Status Protocol, 证书状态在线查询协议,一个实时查询证书是否吊销的方式。请求者发送证书的信息并请求查询,服务器返回正常、吊销或未知中的任何一个状态。证书中一般也会包含一个 OCSP 的 URL 地址,要求查询服务器具有良好的性能。部分 CA 或大部分的自签 CA (根证书)都是未提供 CRL 或 OCSP 地址的,对于吊销证书会是一件非常麻烦的事情。

    版权声明:本文转载自https://blog.csdn.net/hherima/article/details/52469488

  • 信息安全 110 0 1 发布

    介绍定义

    公钥基础设施PKI(Public Key Infrastructure),是一种遵循既定标准的密钥管理平台,它能够为所有网络应用提供加密和数字签名等密码服务及所必需的密钥和证书管理体系,简单来说,PKI就是利用公钥理论和技术建立的提供安全服务的基础设施。PKI技术是信息安全技术的核心,也是电子商务的关键和基础技术。

    目的

    PKI技术能够为网络通信和网络交易,特别是电子政务和电子商务业务,透明地提供一整套安全服务,主要包括身份认证、保密、数据完整性和不可否认性。
    作为一种基础设施,PKI的应用范围非常广泛,并且在不断发展之中,下面给出几个常见的应用场景。

    虚拟专用网络VPN(Virtual Private Network)
    VPN是一种构建在公用通信基础设施上的专用数据通信网络,利用网络层安全协议(如IPSec)和建立在PKI上的加密与数字签名技术来获得机密性保护。安全电子邮件
    电子邮件的安全也要求机密、完整、认证和不可否认,而这些都可以利用PKI技术来实现。目前发展很快的安全/多用途Internet邮件扩充协议S/MIME(Secure/Multipurpose Internet Mail Extensions),是一个允许发送加密和有签名邮件的协议,该协议的实现需要依赖于PKI技术。Web安全
    为了透明地解决Web的安全问题,在两个实体进行通信之前,先要建立SSL(Secure Sockets Layer,安全套接层)连接,以此实现对应用层透明的安全通信。利用PKI技术,SSL协议允许在浏览器和服务器之间进行加密通信。此外,服务器端和浏览器端通信时双方可以通过数字证书确认对方的身份。受益

    用户受益

    通过PKI证书认证技术,用户可以验证接入设备的合法性,从而可以保证用户接入安全、合法的网络中。
    通过PKI加密技术,可以保证网络中传输的数据的安全性,数据不会被篡改和窥探。
    通过PKI签名技术,可以保证数据的私密性,未授权的设备和用户无法查看该数据。

    企业受益

    企业可以防止非法用户接入企业网络中。
    企业分支之间可以建立安全通道,保证企业数据的安全性。

    PKI基本概念公钥加密算法

    公钥加密算法,又叫非对称加密算法或双钥加密算法,是指加密密钥和解密密钥为两个不同密钥的密码算法。

    公钥加密算法使用了一对密钥:一个用于加密信息,另一个则用于解密信息,其中加密密钥公之于众,称为公钥;解密密钥由解密人私密保存,称为私钥。用其中任一个密钥加密的信息只能用另一个密钥进行解密。

    RSA密钥对

    数字证书机制依赖于公共密钥体制,PKI系统中应用最广泛的公共密钥体制为RSA加密系统。

    RSA加密系统使用一个非对称的RSA密钥对,包括一个RSA公钥和一个RSA私钥。当实体申请数字证书时,证书请求中必须包含RSA公钥信息。

    RSA密钥对的模数,即RSA密钥的长度(单位bit)。模数越大,密钥越安全,同时设备生成密钥、加密、解密花费的时间也越长。

    数字指纹

    数字指纹是指通过某种算法对数据信息进行综合计算得到的一个固定长度的数字序列,这个序列有时也称信息摘要,常采用单向哈希算法对原始数据进行散列计算得出数字指纹。

    数字签名

    数字签名是指信息发送方用自己的私钥对原始数据的数字指纹进行加密后所得的数据。

    信息接收者使用信息发送者的公钥对附在原始信息后的数字签名进行解密后,获得数字指纹,然后与自己用同样算法对原始数据计算生成的数据指纹进行匹配,根据匹配结果,便可确定原始信息是否被篡改。

    数字证书

    数字证书是一个经认证机构CA(Certificate Authority)签名的,包含实体公开密钥及相关身份信息的文件,它建立了实体身份信息与其公钥的关联,是使用PKI系统的用户建立安全通信的信任基础。CA对数字证书的签名保证了证书的合法性和权威性。

    数字证书中包含多个字段,包括证书签发者的名称、主体的公钥信息、CA对证书的数字签名、证书的有效期等。

    本地(local)证书和CA(Certificate Authority)证书

    本地证书是CA签发给实体的数字证书;CA证书是CA自身的证书。若PKI系统中存在多个CA,则会形成一个CA层次结构,最上层的CA是根CA,它拥有一个CA“自签名”的证书。

    证书废除列表CRL

    由于实体名称的改变、私钥泄漏或业务中止等原因,需要存在一种方法将现行的证书撤消,即撤消公开密钥及相关的实体身份信息的绑定关系。在PKI中,所使用的这种方法为证书废除列表CRL(Certificate Revocation List)。

    任何一个证书被废除以后,CA就要发布CRL来声明该证书是无效的,并列出所有被废除的证书的序列号。CRL提供了一种检验证书有效性的方式。

    当一个CRL的撤消信息过多时会导致CRL的发布规模变得非常庞大,且随着CRL大小的增加,网络资源的使用性能也会随之下降。为了避免这种情况,允许一个CA的撤消信息通过多个CRL发布出来,并且使用CRL发布点CDP(CRL Distribution Point)来指出这些CRL的位置。

    CRL发布点

    CRL发布点CDP,是数字证书中的信息,它描述了如何获取证书的CRL列表。
    最常用的CDP是HTTP URL和LDAP URL,也可以是其他类型的URL或LDAP目录说明。
    一个CDP包含一个URL或目录说明。

    PKI体系架构PKI架构

    如图1所示,一个PKI体系由终端实体、认证机构、注册机构和证书/CRL存储库四部分共同组成。

    终端实体
    终端实体是PKI产品或服务的最终使用者,可以是个人、组织、设备(如路由器、交换机)或计算机中运行的进程。认证机构CA(Certificate Authority)
    CA是PKI的信任基础,是一个用于签发并管理数字证书的可信实体。其作用包括:发放证书、规定证书的有效期和通过发布CRL确保必要时可以废除证书。注册机构RA(Registration Authority)
    RA是CA的延伸,可作为CA的一部分,也可以独立。RA功能包括个人身份审核、CRL管理、密钥对产生和密钥对备份等。PKI国际标准推荐由一个独立的RA来完成注册管理的任务,这样可以增强应用系统的安全性。证书/CRL存储库
    证书/CRL存储库负责证书和CRL的存储、管理、查询等。认证机构

    认证机构层次

    认证机构是PKI体系的核心,通常采用多层次的分级结构,根据证书颁发机构的层次,可以划分为根CA和从属CA。上级认证机构负责签发和管理下级认证机构的证书,最下一级的认证机构直接面向用户。每一份数字证书都与上一级的数字签名证书相关联,最终通过证书链追溯到一个根认证机构,根CA通常持有一个自签名证书。

    根CA是公钥体系中第一个证书颁发机构,它是信任的起源。根CA可以为其它CA颁发证书,也可以为其它计算机、用户、服务颁发证书。对大多数基于证书的应用程序来说,使用证书的认证都可以通过证书链追溯到根。从属CA必须从根CA或者从一个已由根CA授权可颁发从属CA证书的从属CA处获取证书。

    在建立CA时,从属CA要通过上级CA获得自己的CA证书,而根CA则是创建自签名的证书。

    认证机构类型

    认证机构CA的类型包括以下三种:

    自签名CA:在自签名CA中,证书中的公钥和用于验证证书签名的公钥是相同的。从属CA:在从属CA中,证书中的公钥和用于验证证书签名的公钥是不同的。根CA:根CA是一种特殊的CA,它受到客户无条件地信任,位于证书层次结构的最高层。所有证书链均终止于根CA。根CA必须对它自己的证书签名,因为在证书层次结构中再也没有更高的认证机构。

    认证机构功能

    CA的核心功能就是发放和管理数字证书,包括:证书的颁发、证书的更新、证书的撤销、证书的查询、证书的归档、CRL的发布等。具体描述如下:

    证书申请处理:接收、验证用户数字证书的申请。证书审批处理:确定是否接受用户数字证书的申请。证书颁发处理:向申请者颁发或拒绝颁发数字证书。证书更新处理:接收、处理用户的数字证书更新请求。证书查询和撤销处理:接收用户数字证书的查询、撤销。发布CRL:产生和发布证书废除列表(CRL)。证书的归档:数字证书的归档。密钥的备份和恢复。历史数据归档。注册机构

    RA是数字证书注册审批机构,RA是CA面对用户的窗口,是CA的证书发放、管理功能的延伸,它负责接受用户的证书注册和撤销申请,对用户的身份信息进行审查,并决定是否向CA提交签发或撤销数字证书的申请。
    RA作为CA功能的一部分,实际应用中,通常RA并不一定独立存在,而是和CA合并在一起。RA也可以独立出来,分担CA的一部分功能,减轻CA的压力,增强CA系统的安全性。

    PKI工作机制工作过程

    针对一个使用PKI的网络,配置PKI的目的就是为指定的实体向CA申请一个本地证书,并由设备对证书的有效性进行验证。PKI的工作过程如下:

    实体向注册机构RA提出证书申请。RA审核实体身份,将实体身份信息和公开密钥以数字签名的方式发送给CA。CA验证数字签名,同意实体的申请,颁发证书。RA接收CA返回的证书,通知实体证书发行成功。实体获取证书,利用该证书可以与其它实体使用加密、数字签名进行安全通信。实体希望撤消自己的证书时,向CA提交申请。CA批准实体撤消证书,并更新CRL。工作原理

    PKI的目标就是要充分利用公钥密码学的理论基础,建立起一种普遍适用的基础设施,为各种网络应用提供全面的安全服务。

    对于公钥加密算法,由于公钥是公开的,需要在网上传送,故公钥的管理问题就是公钥加密体制所需要解决的关键问题。目前,PKI系统中引出的数字证书机制就是一个很好的解决方案。PKI的核心技术就围绕着数字证书的申请、颁发、使用与撤销等整个生命周期进行展开。

    证书注册

    证书注册即证书申请,就是一个实体向CA自我介绍并获取数字证书的过程。实体向CA提供身份信息,以及相应的公钥,这些信息将成为颁发给该实体证书的主要组成部分。

    实体向CA提出证书申请,有离线和在线两种方式。离线申请方式下,CA允许申请者通过带外方式(如电话、磁盘、电子邮件等)向CA提供申请信息。在线证书申请有手工发起和自动发起两种方式。以下是常用的证书注册方式:

    PKCS#10方式(离线注册方式)
    当无法通过SCEP协议向CA在线申请证书时,可以使用PKCS#10格式打印出本地的证书申请信息。用户以PKCS#10格式保存证书申请信息到文件中,并通过带外方式发送给CA进行证书申请。SCEP方式(在线注册/下载方式)
    通过简单证书注册协议SCEP(Simple Certification Enrollment Protocol),利用HTTP协议与CA或RA通信,发送证书注册请求或证书下载请求消息,下载CA/RA证书、本地证书,或者申请本地证书。SCEP方式是最常用的证书自动注册方式。自签名证书
    PKI设备为自己颁发一个自签名证书,即证书签发者和证书主题相同。证书更新

    设备在证书即将过期前,先申请一个证书作为“影子证书”,在当前证书过期后,影子证书成为当前证书,完成证书更新功能。

    申请“影子证书”的过程,实质上是一个新的证书注册的过程。

    证书更新功能需要CA服务器的支持,即CA服务器必须支持证书更新功能。

    证书下载

    证书下载是指终端实体通过SCEP协议,向CA服务器查询并下载已颁发的证书,或者通过CDP指定机制和地址,下载已颁发的证书。该证书可以是自己的证书,也可以是CA证书,或其他终端实体的证书。

    证书撤销

    由于用户身份、用户信息或者用户公钥的改变、用户业务中止等原因,用户需要将自己的数字证书撤消,即撤消公钥与用户身份信息的绑定关系。在PKI中,CA撤销证书使用的方法为证书废除列表CRL,终端实体撤销自己的证书是通过带外方式申请的。

    为了撤销自己的证书,终端实体必须采用带外方式通知CA服务器管理员。

    管理员要求终端实体提供自己的Challenge Password(Challenge Password在证书注册时已作为PKCS10证书请求的属性发给了CA)。

    如果终端实体提供的Challenge Password与CA服务器保存的一致,CA发布CRL来撤销证书。

    CRL下载

    CA/RA不会主动把CRL发布给终端实体,而是由终端实体主动发起CRL查询。有两种下载CRL的方法:CDP方式、SCEP方式。

    CA如果支持CDP,在为终端实体颁发证书时,把CRL发布点的URL地址编码成CDP属性封装在证书中,终端实体根据CDP来下载CRL。

    如果证书中未携带CDP信息,并且设备本地也没有配置CDP的URL地址,则设备通过SCEP协议向CA服务器请求CRL。终端实体通过SCEP协议获取证书时,以证书签发者名字和证书序列号作为查询关键字。

    证书状态检查方式

    当终端实体验证对端证书时,经常需要检查对端证书是否有效,例如对端证书是否过期、是否被加入证书黑名单中,即检查证书的状态。通常终端实体检查证书状态的方式有三种:CRL方式、OCSP方式、None方式。

    CRL方式
    如果CA支持CDP,那么当CA签发证书时,在证书中会包含CDP信息,描述了获取该证书CRL的途径和方式。终端实体利用CDP中指定的机制和地址来定位和下载CRL。

    如果PKI域下配置了CDP的URL地址,该地址将覆盖证书中携带的CDP信息,终端实体使用配置的URL来获取CRL。

    在线证书状态协议OCSP(Online Certificate Status Protocol)方式
    如果CA不支持CDP,即证书中没有指定CDP,并且PKI域下也没有配置CRL的URL地址,终端实体可以使用OCSP协议检查证书状态。

    None方式
    如果终端实体没有可用的CRL和OCSP服务器,或者不需要检查对端证书状态,可以采用None方式,即不检查证书是否被撤销。

    证书合法性验证

    终端实体获取对端证书后,当需要对对端进行证书认证时,例如需要与对端建立安全隧道或安全连接,通常需要验证对端证书和证书签发者的合法性。如果证书签发者的证书无效或过期,则由该CA签发的所有证书都不再有效。但在CA证书过期前,设备会自动更新CA/RA证书,异常情况下才会出现CA证书过期现象。

    为完成证书验证,除了需要对端证书外,本地设备需要下面的信息:信任的CA证书、CRL、本端数字证书及其私钥、证书认证相关配置信息。

    证书验证的主要过程如下:

    使用CA证书的公钥验证认证机构的签名是否正确。根据证书的有效期,验证证书是否过期。检查证书的状态,即通过CRL、OCSP、None等方式检查证书是否被撤销。证书链验证

    为验证一个数字证书的合法性,首先需要获得签发这个证书的CA的公钥(即获得CA证书),以便检查该证书上CA的签名。一个CA可以让另一个更高层次的CA来证明其数字证书的合法性,这样顺着证书链,验证数字证书就变成了一个叠代过程,最终这个链必须在某个“信任点”(一般是持有自签名证书的根CA或者是实体信任的中间CA)处结束。

    所谓的证书链,是指从终端实体证书到根证书的一系列可信任证书构成的证书序列。任何终端实体,如果它们共享相同的根CA或子CA,并且已获取CA证书,都可以验证对端证书。一般情况下,当验证对端证书链时,验证过程在碰到第一个可信任的证书或CA机构时结束。

    证书链的验证过程是一个从目标证书(待验证的实体证书)到信任点证书逐层验证的过程。

    版权声明:转载自:http://blog.sina.com.cn/s/blog_afd4c4ef0102w20n.html

  • Backbone 63 0 1 发布

    Backbone视图可以使用JavaScript模板,根据模型数据的逻辑处理向用户展示相应的界面。可以监听模型的change事件,并在回调函数绑定视图的render()方法,就可以在不重绘整个页面的情况下,更新视图。

    创建视图

    可以通过扩展Backbone.View对象创建一个视图:

    var app = app || {}; app.MenuListView = Backbone.View.extend({ tagName: 'ul', className: 'menu-list', template: function() { return _.template('<li><%= title %></li>'); }, render: function() { this.$el.html(this.template()({title: "Menu List"})); return this; }, initialize: function() { this.render(); $('body').append(this.$el); } }); app.menulist = new app.MenuListView(); console.log(app.menulist.el); console.log(app.menulist.$el);

    打印值如下:

    app.menulist.el:

    app.menulist.$el:

    初始化

    如果视图定义了initialize()初始化函数,在创建视图时,它将立即被调用。

    视图与模型

    创建视图时,可以通过传入model或collection属性和值,将某一模型或集合直接注册到视图中:

    var app = app || {}; app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); app.MenuListView = Backbone.View.extend({ tagName: 'ul', className: 'menu-list', template: function() { return _.template('<li><%= title %></li>'); }, render: function() { this.$el.html(this.template()({title: "Menu List"})); return this; }, initialize: function() { //console.log('视图初始化'); this.render(); $('body').append(this.$el); } }); app.menulist = new app.MenuListView({ collection: app.menus }); console.log(app.menulist.collection);

    打印如下:

    视图与DOM

    我们希望通过使用Backbone视图生成页面文档,将页面展示给用户,就必须将视图与DOM关联起来,通过操作视图改变DOM。

    Backbone提供两种方式关联视图和DOM:

    创建

    创建视图时创建一个新元素,然后将该元素插入至DOM。

    引用

    视图直接引用页面已经存在的元素。

    创建

    Backbone视图创建元素时只需要使用tagName、id、className属性。此Backbone视图对象的el属性是一个指向该元素的引用。

    tagName默认值为div。

    app.MenuListView = Backbone.View.extend({ tagName: 'ul', //元素标签类型,必选,未设置时默认为div className: 'menu-list', //元素class,可选 id: 'menus', //元素id,可选 initialize: function() { $('body').append(this.$el); } }); app.menulist = new app.MenuListView(); console.log(app.menulist.el); //logs: <ul id="menus" class="menu-list"></ul>

    引用
    可以为Backbone视图对象传入一个el属性,来匹配页面文档已存在的元素。

    app.MenuListView = Backbone.View.extend({ el: 'body', //值为CSS选择器 initialize: function() { } }); app.menulist = new app.MenuListView(); console.log(app.menulist.el); //logs: <body>...</body>

    我们也可以在创建视图时,为对象el属性设置值:

    app.menulist = <span class="hljs-keyword">new</span> app.MenuListView({el: <span class="hljs-string">'body'</span>});

    el属性值为CSS选择器,Backbone视图对象将根据此选择器匹配页面元素。

    视图属性与方法

    要深入理解Backbone视图,必须理解视图一些重要的属性和方法。

    el

    el是视图的一个核心属性,也是使用视图对象时必然会用到的一个属性。

    el是DOM元素的一个引用,所有视图都有其el属性。视图可以通过使用el构成它的元素内容,在触发浏览器最少次数重排和重绘的情况下,将所有内容一次性插入文档DOM。

    $el、$()

    对视图和DOM操作,经常要用到jQuery函数,Backbone通过为视图定义$el属性和$()函数,为我们操作视图和DOM提供了很多便利。视图的$el属性等价于$(View.el),$(selector)等价于$(View.el).find(selector)。

    setElement()
    Backbone提供setElement()方法,支持将现有Backbone视图应用于不同的DOM元素,调用该方法将创建一个缓存$el引用,并且将视图委托事件从旧元素移动到新元素。

    var MyView = Backbone.View.extend({ events: { click: function(e) { console.log(myView.$el.html()); } } }); var btn1 = $('<button>button 1</button>'); var btn2 = $('<button>button 2</button>'); //视图元素el是btn1 var myView = new MyView({el: btn1}); btn1.trigger('click'); //logs: button 1 //视图元素el指向btn2 myView.setElement(btn2); btn1.trigger('click'); btn2.trigger('click'); //logs: button 2 console.log(myView.$el.html);// button 2

    渲染模板-render()

    render()函数是一个可选函数,需要我们主动调用,通常我们在里面实现根据模型属性渲染视图模板,生成HTML标记;然后使用el或$el属性,将这些HTML标记设置为视图的el属性所引用的 DOM元素的HTML内容。

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); this.on('reset', function(cols, options) { console.log('重置集合'); }, this); this.on('add', function(model) { console.log('添加模型,cid: ' + model.cid); }); this.on('change', function(model) { console.log('模型属性改变为: ' + model.get('name'), model); }); this.on('change:name', function(model) { console.log('模型name属性改变为: ' + model.get('name')); }); this.on('remove', function(model) { console.log('移除模型: ' + model.cid); }); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); app.MenuItemView = Backbone.View.extend({ tagName: 'li', className: 'menu-item', template: function() { return _.template('<a title="<%= title %>"><%= name %></a>'); }, render: function() { this.$el.html(this.template()(this.model.toJSON())); return this; } }); app.MenuListView = Backbone.View.extend({ tagName: 'ul', className: 'menu-list', template: function() { return _.template(''); }, render: function() { //遍历集合,集合中每个模型对应一个菜单项 _.each(this.collection.models, function(model) { //为每个集合模型,创建一个新菜单项视图实例 var menuItemView = new app.MenuItemView({model: model}); //在父视图-菜单列表视图中添加子菜单-菜单项视图 this.$el.append(menuItemView.render().el); }, this); console.log(this.el); return this; }, initialize: function() { this.render(); $('body').append(this.$el); } }); app.menulistview = new app.MenuListView({ collection: app.menus });

    控制台打印值如下:

    页面效果如图:

    我们通常在render()函数底部返回this以开启链式调用,该视图可以在其他父视图里被重用。

    Events对象

    Backbone提供events对象支持我们通过设置在el下的自定义CSS选择器、事件类型和事件监听器,为DOM元素绑定事件。

    app.MenuItemView = Backbone.View.extend({ tagName: 'li', className: 'menu-item', events: { 'click .menu': 'openMenu', 'dblclick .menu': 'edit' }, template: function() { return _.template('<a class="menu" title="<%= title %>"><%= name %></a>'); }, render: function() { this.$el.html(this.template()(this.model.toJSON())); return this; }, initialize: function() { this.dbltimer = null; }, openMenu: function(e) { if (this.dbltimer) { clearTimeout(this.dbltimer); this.dbltimer = null; } this.dbltimer = setTimeout(function(){ console.log('opened'); },300); }, edit: function(e) { if (this.dbltimer) { clearTimeout(this.dbltimer); this.dbltimer = null; } console.log('edit'); } }); app.MenuListView = Backbone.View.extend({ tagName: 'ul', className: 'menu-list', template: function() { return _.template(''); }, render: function() { _.each(this.collection.models, function(model) { var menuItemView = new app.MenuItemView({model: model}); this.$el.append(menuItemView.render().el); }, this); // this.$el.html(this.template()({title: "Menu List"})); console.log(this.el); return this; }, initialize: function() { this.render(); $('body').append(this.$el); } }); app.menulistview = new app.MenuListView({ collection: app.menus });

    若没有设置CSS选择器,则默认为el所引用DOM元素绑定事件。

    移除视图-remove()

    调用View.remove(),从DOM中移除一个视图。同时将调用stopListening来移除通过 listenTo绑定在视图上的所有事件。

    app.menulistview.remove();

    声明: 本文转载自熊建刚的博客

  • Backbone 53 0 1 发布

    Backbone集合是模型的有序组合,通过拓展Backbone.Collection对象来创建集合:

    在创建集合时,通过model属性设置集合中的模型类型,实例化集合时,可以传入对象或数组,它们将被自动转换为通过model设置的模型类型。

    var app = app || {}; app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近联系人', status: 'active' }, idAttribute: 'idx' }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel }); app.menu = new app.MenuModel({ status: 'inactive', idx: '001' }); app.menus = new app.MenuCollection([app.menu]); console.log(app.menus.length); //1 初始化

    在创建集合时,可以设置initialize函数,在实例化集合时,将自动调用执行initialize函数:

    app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); app.menus = new app.MenuCollection([app.menu]);

    打印值如下:

    集合属性方法

    models
    此属性返回集合中模型数组:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); console.log(app.menus.models.length); //2

    toJSON()
    Collection.toJSON()方法返回集合中所有模型attributes对象拷贝副本的JSON格式对象数组:

    console.log(app.menus.toJSON());

    集合与模型

    Backbone.Collection对象允许我们通过多种方法对模型进行管理。

    添加/移除模型

    集合创建以后,可以使用add()和remove()方法添加或移除集合中的模型:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); var menu1 = new app.MenuModel({ name: '最近会话', status: 'active' }); var menu2 = new app.MenuModel({ name: '通讯录', status: 'inactive' }); var menu3 = new app..MenuModel({ name: '公告', status: 'inactive' }); app.menus = new app.MenuCollection([menu1, menu2]); console.log(app.menus);

    app.menus打印值如下:

    app.menus.add(menu3); console.log(app.menus.length);//3 app.menus.remove(menu2); console.log(app.menus.length);//2 检索模型

    get()
    调用collection.get()方法,传入一个id或者cid从集合中检索一个模型:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); var a = app.menus.get(1); var b = app.menus.get('c2'); console.log(a === b); //true

    at()
    调用collection.at()方法,传入模型在集合中的索引值,at始终根据模型在插入集合时的索引返回值:

    var a = app.menus.get(1); var c = app.menus.at(0); console.log(a === c); //true

    where()
    调用collection.where()方法,传入模型属性,返回符合的模型数组:

    var d = app.menus.where({name: '最近会话'}); console.log(d);

    打印值如下:

    findWhere()
    findWhere()方法与where()方法调用方式一样,只是findWhere()方法返回匹配传入属性的第一个模型,而不是返回一个模型数组:

    var d = app.menus.where({name: '最近会话'}); console.log(d); 重置刷新集合

    set()

    Backbone不仅支持添加或移除模型,还提供一次性更新整个集合:调用Collection.set()方法,接收一个模型数组,将执行更新集合所必要的添加、移除和更新操作。
    如果列表中的一个模型尚不在集合中,那么它将被添加; 如果模型已经在集合中,其属性将被合并; 并且如果集合包含不存在于列表中的任何模型,他们将被删除。 以上所有将触发相应的”add”, “remove”, 和 “change”事件。 返回集合中的模型。另外 可以设置选项:{add: false}, {remove: false}, 或 {merge: false},将相应操作禁用,实现自定义行为。

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); app.menus.set([ {id:1, name: '最近聊天'}, {id:3, name: '公告'} ]); console.log(app.menus.length); //2 console.log(app.menus);

    打印值如下:

    reset()
    调用Collection.reset()方法,可以重置整个集合:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); this.on('reset', function(cols, options) { console.log(cols, options.previousModels); }); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); app.menus.reset([ {id:1, name: '最近聊天'} ]); console.log(app.menus.length); //1

    打印值如下:

    调用Collection.reset()方法不会触发add和remove事件,但会触发reset事件,我们可以通过传入空参数来清空集合;监听reset事件时可以从options.previousModels访问之前的模型数组。

    事件监听

    集合是模型的有序组合,在集合中添加或移除模型时会触发add或remove事件,我们可以监听这些事件,进行相应数据逻辑处理;同时,可以在集合的任意模型属性上绑定change事件,监听模型属性的变化。

    一般,事件监听在集合初始化时绑定,第一个参数为监听事件类型,第二个参数为监听回调,第三个参数为回调的上下文环境(可选),通常传入指向当前集合的this。

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', status: 'inactive' } }); app.MenuCollection = Backbone.Collection.extend({ model: app.MenuModel, initialize: function() { console.log('集合初始化成功'); this.on('reset', function(cols, options) { console.log('重置集合'); }, this); this.on('add', function(model) { console.log('添加模型,cid: ' + model.cid); }, this); this.on('change', function(model) { console.log('模型属性改变为: ' + model.get(name')); }); this.on('change:name', function(model) { console.log('模型name属性改变为: ' + model.get('name')); }); this.on('remove', function(model) { console.log('移除模型: ' + model.cid); }); } }); var menus = [ { id: 1, name: '最近会话', status: 'active' }, { id: 2, name: '通讯录', status: 'inactive' } ] app.menus = new app.MenuCollection(menus); app.menus.set([ {id:1, name: '最近聊天'}, {id:3, name: '公告'} ]); app.menus.reset([ {id:1, name: '最近聊天'} ]); //logs: //模型name属性改变为: 最近聊天 //模型属性改变为: 最近聊天 //移除模型: c3 //添加模型,cid: c4 //重置集合

    Backbone事件除了上文的on()方法,还可以使用once()方法,与jQuery的once类似,监听回调只执行一次。

    声明: 本文转载自熊建刚的博客

  • Backbone 50 0 1 发布

    Backbone模型包含应用程序的数据和与数据相关的逻辑处理,我们通过拓展Backbone.Model对象来创建模型:

    var app = app || {}; app.MenuModel = Backbone.Model.extend({}); 构造函数初始化

    当创建一个模型的新实例时,其构造函数,即initialize()方法会被调用:

    app.MenuModel = Backbone.Model.extend({ initialize: function() { console.log('Initialize the Model.'); } }); app.menu = new app.MenuModel(); 默认值

    创建Backbone模型时,Backbone允许我们为模型设置一组默认值,通过defaults属性即可设置;当实例化模型时,可传入一个参数对象,此对象即为模型的数据,传入参数值将覆盖所设置的默认值。

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' } }); app.menu = new app.MenuModel({ status: 'inactive' }); console.log(JSON.parse(app.menu)); //{"title": "this is a menu", "name": "最近会话", "status": "inactive"} 属性操作

    Model.get()

    Model.get()方法用于访问模型的属性:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' } }); app.menu = new app.MenuModel({ status: 'inactive' }); console.log(app.menu.get('status'));//inactive

    Model.set()

    Model.set()方法用于设置模型的一个或多个属性值:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' } }); app.menu = new app.MenuModel({ status: 'inactive' }); console.log(app.menu.get('status'));//inactive app.menu.set({ status: 'active', title: 'menu' }); console.log(app.menu.get('status'));//active

    Model.attributes

    Backbone模型的attributes属性,是包含模型状态的一个内部散列表,使用Model.attributes可直接访问该模型所有数据,其通常以服务器返回的JSON对象数据形式存在:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' } }); app.menu = new app.MenuModel({ status: 'inactive' }); console.log(app.menu.attributes); //Object {status: "inactive", title: "this is a menu", name: "最近会话"}

    注:强烈建议使用set方法更新模型attributes对象而不是直接修改attributes对象。

    Model.toJSON()

    Model.toJSON()方法返回模型attributes对象拷贝副本的JSON格式对象:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' } }); app.menu = new app.MenuModel({ status: 'inactive' }); console.log(app.menu.toJSON()); //Object {status: "inactive", title: "this is a menu", name: "最近会话"} console.log(JSON.stringify(app.menu)); //{"status":"inactive","title":"this is a menu","name":"最近会话"}

    注:当给JSON.stringify()传递带toJSON()方法的对象时,其处理的是该对象执行toJSON()方法后返回的值,而不是原始传入的对象,如:

    var a = { data: { name: '惊鸿', title: 'sj' }, toJSON: function() { return 'sds'; } }; console.log(JSON.stringify(a));//"sds"
    模型id与cidid

    id是模型的特殊属性,可以是任意字符串(整型 id 或 UUID)。在属性中设置的 id 会被直接拷贝到model属性上。 也可以在集合(collections)中通过 id 获取特定model。

    idAttribute

    模型的唯一标识符,被储存在 id 属性下,可以通过设置Model的idAttribute与一个唯一值key对应,从而形成一个从该key到模型id的一个映射。

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' }, idAttribute: 'idx', //形成从idx到id的映射 initialize: function() { } }); app.menu = new app.MenuModel({ idx: '001' }); console.log(app.menu.id);//001

    cid

    cid,即客户端id,是model创建时自动产生的唯一标识符。 客户端id在model尚未保存到服务器之前便存在,此时model可能仍不具有最终的id,但已经需要在用户界面可见。

    console.log(app.menu.cid);//c1或其他 模型变化监听

    通过监听模型上change事件,可以监测到模型的变化,既可以监听整个模型的改变,也可以监听模型单个属性的变化;通常在模型initialize()函数中添加监听器:

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', name: '最近会话', status: 'active' }, initialize: function() { //监听模型变化 this.on('change', function(model) { console.log(model); console.log('模型已改变'); }); //监听模型单个属性变化 this.on('change:name', function(model) { console.log('菜单名已修改'); }); } }); app.menu = new app.MenuModel(); app.menu.set({ title: 'menu', status: 'inactive' }); console.log(app.menu.get('title'));//menu console.log(app.menu.get('status'));//inactive app.menu.set({ name: '常用联系人', }); console.log(app.menu.get('name'));//常用联系人 验证

    调用Model.validate()方法进行模型验证,在设置属性值之前对其进行验证,默认,在使用save()方法或调用set()方法时设置了{validate: true}参数以持久化数据模型均会触发验证;验证通过将不返回值,验证失败将触发"invalid"事件, 此方法返回一个错误值,并用此方法返回的值设置模型上的validationError属性,同时模型上的属性值不会持久化到服务器: app.MenuModel = Backbone.Model.extend({

    app.MenuModel = Backbone.Model.extend({ defaults: { title: 'this is a menu', //name: '最近会话', status: 'active' }, validate: function(attr) { if (attr.name == undefined ) { //返回错误值 return '菜单名不能为空'; } }, initialize: function() { //监听invalid事件 this.on('invalid', function(model, err) { console.log('title: ' + model.get('title'), '; error: ' + err); }); } }); app.menu = new app.MenuModel(); console.log((app.menu).toJSON()); //Object {title: "this is a menu", status: "active"} //set()方法且设置{validate: true}参数 app.menu.set({ title: 'menu', }, {validate: true}); console.log(app.menu.get('title'));//this is a menu //set()方法但不设置{validate: true}参数 app.menu.set({ title: 'menu', }); console.log(app.menu.get('title'));//menu

    注:validate函数接收的参数对象是在执行set()或save()方法后模型的值对象。

    声明: 本文转载自熊建刚的博客