首页 文章 精选 留言 我的

精选列表

搜索[快速],共10012篇文章
优秀的个人博客,低调大师

混合云的快速指南

混合云模型为企业提供了一种利用公共云和私有云优势的方法。 混合云带来了两全其美的优势。 在最初的几年中,云计算技术日渐盛行,得到了IT和金融专业人士的称赞。但是,那些使用极其敏感或关键任务系统的人仍然不愿意使用该技术。企业开始做的是保持平衡并采用混合模型,而不是全盘采用内部部署数据中心或公共系统。 混合云是一种利用私有云和公共云在同一组织内执行不同功能的集成云服务。所有云计算服务应在不同程度上提供一定的效率,但公共云服务可能比私有云更具成本效益和可扩展性。借助混合云,组织可以通过将公共云服务用于所有不敏感的操作,而仅在需要时依靠私有云,从而确保所有平台无缝集成,从而最大限度地提高效率。 混合云是指由本地基础设施、私有云服务和公共云(例如Amazon Web Services(AWS)或Microsoft Azure)组成的混合计算、存储和服务环境,其中各个平台之间进行了编排。在数据中心中结合使用公共云,本地计算和私有云意味着企业拥有混合云基础设施。 混合云是一种计算环境,它通过允许在数据和应用程序之间共享而将公共云和私有云结合在一起。当计算和处理需求波动时,混合云计算使企业能够将其内部部署基础设施无缝扩展到公共云,以处理任何溢出。无需让第三方数据中心访问其全部数据。组织可以在基本和非敏感计算任务上获得公共云的灵活性和计算能力,同时将关键业务应用程序和数据安全地保留在公司防火墙的内部。 混合云通常是指公共云服务和内部私有云的结合;但是,混合云也可以由不同提供商提供的两个公共云组成,甚至可以由云和传统IT组成。实际上,当前将传统IT基础设施上的现有系统与公共云服务相结合的设置是混合云最常见的用例。 混合云使企业可以使用更广泛的IT服务组合。例如,企业可能在私有云中运行关键任务工作负载,但使用公共云提供商的数据库或归档服务。 这意味着,公共云的即付即用的可扩展性非常适合繁忙或不可预测的流量,并且可以降低IT成本。当企业需要增强安全性并需要对关键业务应用程序和数据进行最终控制时,请合并私有云。利用单租户专用服务器的性能,企业可以在裸机上获得超快的性能、安全性和可靠性。混合云方法可以统一这两种方法,可以为企业提供经济高效的安全解决方案。 混合云用例是大数据处理。例如,一家公司可以使用混合云存储来保留其累积的业务、销售、测试和其他数据,然后在公共云中运行分析查询,这可以扩展Hadoop或其他分析集群以支持要求苛刻的分布式计算任务。 尽管有好处,混合云计算仍会带来技术,业务和管理方面的挑战。这需要本地IT员工和云架构师的大量专业知识。诸如数据库,帮助台系统和其他工具之类的其他软件的实施可能会使私有云进一步复杂化。此外,企业对私有云的技术支持负全部责任,并且必须适应公共云API的任何更改以及服务随时间的变化。 此外,可能存在潜在的连接问题、服务等级协议(SLA)违规以及其他可能的服务中断。为了减轻这些风险,组织可以设计与多个公共云提供商进行互操作的混合云工作负载。在某些情况下,企业需要重新设计混合云计划的工作负载,以解决特定公共云提供商的API。 迁移到混合云可以节省资金,并使企业更高效、,更敏捷。与私有云相比,公共云可能会提供更重要的规模经济,例如集中管理,因此具有更高的成本效率。因此,混合云使企业可以尽可能多地访问这些节省的业务功能,同时仍保持敏感操作的安全性。

优秀的个人博客,低调大师

GraphQL快速入门教程

摘要: 体验神奇的GraphQL! 原文:GraphQL 入门详解 作者:MudOnTire Fundebug经授权转载,版权归原作者所有。 GraphQL简介 定义 一种用于API调用的数据查询语言 核心思想 传统的api调用一般获取到的是后端组装好的一个完整对象,而前端可能只需要用其中的某些字段,大部分数据的查询和传输工作都浪费了。graphQL提供一种全新数据查询方式,可以只获取需要的数据,使api调用更灵活、高效和低成本。 特点 需要什么就获取什么数据 支持关系数据的查询 API无需定义各种路由,完全数据驱动 无需管理API版本,一个版本持续演进 支持大部分主流开发语言和平台 强大的配套开发工具 使用方法 下面我们通过搭建一个SpaceX的新闻网站来直观学习graphQL的基本使用方法,所有数据由 官方API 获得。 GraphQL服务端 服务端采用node + express。新建一个node项目,安装如下依赖: $ npm i graphql express-graphql express axios 创建入口文件 server.js,里面创建express服务。使用graphQL我们只需要设置一个路由,所有的请求都由这个graphQL的request handler处理: const express = require("express"); const graphqlHTTP = require("express-graphql"); const schema = require("./schema"); const app = express(); app.use( "/graphql", graphqlHTTP({ schema, graphiql: true }) ); const PORT = process.env.PORT || 5000; app.listen(PORT, () => console.log(`Server started on port ${PORT}`)); graphqlHTTP是grapql的http服务,用于处理graphql的查询请求,它接收一个options参数,其中schema是一个 GraphQLSchema实例,我们接下来定义,graphiql设置为true可以在浏览器中直接对graphQL进行调试。更多express-graphql的用法请参考 Github express-graphql。 schema 接下来我们定义schema,schema意为‘模式’,其中定义了数据模型的结构、字段的类型、模型间的关系,是graphQL的核心。 新建schema.js文件,首先定义两个数据模型:LaunchType(发射)和 RocketType(火箭)。注意字段的数据类型需要使用GraphQL定义的,不能使用js中的基本数据类型。 const { GraphQLObjectType, GraphQLInt, GraphQLString, GraphQLBoolean, GraphQLList, GraphQLSchema } = require("graphql"); const LaunchType = new GraphQLObjectType({ name: "Launch", fields: () => ({ flight_number: { type: GraphQLInt }, mission_name: { type: GraphQLString }, launch_date_local: { type: GraphQLString }, launch_success: { type: GraphQLBoolean }, rocket: { type: RocketType } }) }); const LaunchType = new GraphQLObjectType({ name: "Rocket", fields: () => ({ rocket_id: { type: GraphQLString }, rocket_name: { type: GraphQLString }, rocket_type: { type: GraphQLString } }) }); 有了数据模型之后,我们需要从数据库或者第三方API获取数据,在此我们从spacex的官方API获取。我们需要定义一个root query,root query做为所有查询的入口,处理并返回数据,更多请参考 GraphQL Root fields & resolvers。 在 schema.js中增加代码: const axios = require("axios"); const RootQuery = new GraphQLObjectType({ name: "RootQueryType", fields: { launches: { type: new GraphQLList(LaunchType), resolve(parent, args) { return axios .get("https://api.spacexdata.com/v3/launches") .then(res => res.data); } } } }); module.exports = new GraphQLSchema({ query: RootQuery }); 查询列表 完成这一步,服务端api基本搭建完成!我们看一下效果,在浏览器中输入 http://localhost:5000/graphql 将打开 Graphiql(生产环境建议禁用): 我们可以只查询所有的 flight_number: 或者更多的属性: 是不是很简单很神奇! 单个查询 我们也可以通过传入参数查询单条信息: const RootQuery = new GraphQLObjectType({ name: "RootQueryType", fields: { launch: { type: LaunchType, args: { flight_number: { type: GraphQLInt } }, resolve(parent, args) { return axios .get( `https://api.spacexdata.com/v3/launches/${ args.flight_number }` ) .then(res => res.data); } } } }); 结果: 推荐大家使用Fundebug,一款很好用的BUG监控工具~ GraphQL前端 刚刚我们都是用GraphiQL在浏览器调用接口,接下来我们看一下在前端页面中怎么调用graphql服务。前端我们使用react。 在项目根目录初始化react项目: $ npx create-react-app client 为了便于调试,在package.json中增加scripts: "start": "node server.js", "server": "nodemon server.js", "client": "npm start --prefix client", "dev":"concurrently \"npm run server\" \"npm run client\" " 样式我们使用bootswatch中的一款主题: GraphQL的客户端有多种实现,本次项目使用 Apollo,最流行的GraphQL Client。更多client请参考 GraphQL Clients。 安装依赖 安装如下依赖: $ cd client $ npm i apollo-boost react-apollo graphql 其中 apollo-boost 是apollo client本身,react-apollo 是react视图层的集成,graphql 用于解析graphql的查询语句。 设置client 修改App.js内容如下: import React, { Component } from "react"; import ApolloClient from "apollo-boost"; import { ApolloProvider } from "react-apollo"; import "./theme.css"; import "./App.css"; import logo from "./spacex-logo-light.png"; const client = new ApolloClient({ uri: "http://localhost:5000/graphql" }); class App extends Component { render() { return ( <ApolloProvider client={client}> <div className="container"> <img src={logo} id="logo" /> </div> </ApolloProvider> ); } } export default App; 和redux使用<Provider>传递store类似,react-apollo 通过 <ApolloProvider>将apollo client向下传递。 实现query 接着我们来实现显示launches的component,新增文件 components/Launches.js: import React, { Component, Fragment } from "react"; import gql from "graphql-tag"; import { Query } from "react-apollo"; import LaunchItem from "./LaunchItem"; const LAUNCHES_QUERY = gql` query LaunchesQuery { launches { flight_number mission_name launch_date_local launch_success } } `; export class Launches extends Component { render() { return ( <Fragment> <h1 className="display-4 my-3">Launches</h1> <Query query={LAUNCHES_QUERY}> {({ loading, error, data }) => { if (loading) return <h4>Loading...</h4>; if (error) console.log(error); return ( <Fragment> {data.launches.map(launch => ( <LaunchItem key={launch.flight_number} launch={launch} /> ))} </Fragment> ); }} </Query> </Fragment> ); } } export default Launches; query语句通过 graphql-tag 定义,传入 <Query> 执行获取数据并传入 LaunchItem 显示。 components/LaunchItem.js: import React from "react"; export default function LaunchItem({ launch: { flight_number, mission_name, launch_date_local, launch_success } }) { return ( <div className="card card-body mb-3"> <div className="col-md-9"> <h4>Mission: {mission_name}</h4> <p>Date: {launch_date_local}</p> </div> <div className="col-md-3"> <button className="btn btn-secondary">Launch Details</button> </div> </div> ); } 查询语句通过graphql-tag定义,然后传入<Query>执行。 运行 由于本地调试,client和server分别运行在不同的端口,所以需要先进行跨域处理,使用 cors。 // server.js const cors = require('cors'); app.use(cors()); 效果 好了,大功告成,我们来看一下效果: 结语 今天就主要介绍GraphQL工程的搭建和GraphQL Query的使用,更多关于GraphQL的内容比如 Mutation下次有空会跟大家逐步讲解。 本文灵感来源:Youtube@Traversy Media,感谢 本文Demo Github地址:Github@MudOnTire 本文Demo线上展示:Heroku@graphql-spacex-launches 关于Fundebug Fundebug专注于JavaScript、微信小程序、微信小游戏、支付宝小程序、React Native、Node.js和Java线上应用实时BUG监控。 自从2016年双十一正式上线,Fundebug累计处理了10亿+错误事件,付费客户有阳光保险、核桃编程、荔枝FM、掌门1对1、微脉、青团社等众多品牌企业。欢迎大家免费试用!

优秀的个人博客,低调大师

借助URLOS快速安装AliSQL

环境需求 最低硬件配置:1核CPU,1G内存(1+1)提示:如果你的应用较多,而主机节点的硬件配置较低,建议在部署节点时开通虚拟虚拟内存; 生产环境建议使用2G或以上内存; 推荐安装系统:Ubuntu-16.04、Ubuntu-18.04、CentOS7.X、Debian9X的64位的纯净的操作系统; URLOS安装 curl -LO www.urlos.com/iu && sh iu AliSQL安装流程 登录URLOS系统后台,在应用市场中选择别名并搜索“AliSQL”,找到之后,选择合适的版本点击安装按钮 填写服务名称、选择运行节点、服务端口、选择智能部署 填写数据库root用户密码 然后点击“提交”按钮,等待部署完成。

优秀的个人博客,低调大师

借助URLOS快速安装Ruby

环境需求 最低硬件配置:1核CPU,1G内存(1+1)提示:如果你的应用较多,而主机节点的硬件配置较低,建议在部署节点时开通虚拟虚拟内存; 生产环境建议使用2G或以上内存; 推荐安装系统:Ubuntu-16.04、Ubuntu-18.04、CentOS7.X、Debian9X的64位的纯净的操作系统; URLOS安装 curl -LO www.urlos.com/iu && sh iu Ruby安装流程 登录URLOS系统后台,在应用市场中搜索“Ruby”,找到之后,直接点击安装按钮 填写服务名称、选择运行节点、选择智能部署 填写ssh密码(这里的密码是root密码) 然后点击“提交”按钮,等待部署完成;

优秀的个人博客,低调大师

快速入门 WePY 小程序

一、WePY介绍 WePY 是腾讯参考了Vue 等框架对原生小程序进行再次封装的框架,更贴近于 MVVM 架构模式, 并支持ES6/7的一些新特性。 二、WePY 使用 1、WePY的安装或更新都通过npm进行: npm install -g wepy-cli //全局安装或更新WePY命令行工具(wepy脚手架): wepy-cli wepy -v //查看wepy-cli版本 wepy init standard <projectName> //新建wepy小程序项目,1.7.0之前的版本使用:wepy new myproject wepy list //查看项目模板 cd <projectName> //切换至项目目录 npm install //安装依赖 wepy build --watch //开启实时编译 git base here: (1)全局安装或更新 wepy-cli: (2)查看 wepy-cli 版本: (3)创建 wepy 小程序项目: (4)切换至项目目录,安装依赖: (5)开启实时编译 2、代码高亮WebStorm/PhpStorm(其他工具参见:wepy官网代码高亮) (1)打开Settings,搜索Plugins,搜索Vue.js插件并安装。 (2) 打开Settings,搜索File Types,找到Vue.js Template,在Registered Patterns添加*.wpy,即可高亮。 3、代码优化 (1)代码规范 wepy标签和原生一样 自定义组件命名应避开:原生组件名(input、button、view、repeat等)、WePY的辅助标签<repeat> 变量/方法名尽量使用驼峰式命名,避免使用$开头($开头的标识符是WePY内建属性/方法,可在js中以this.的方式直接使用,具体请参考API文档) app、pages、components文件名的后缀为.wpy,外链的文件可以是其它后缀。 具体请参考wpy文件说明。 支持ES6/7的一些新特性,框架在ES6(ECMAScript 6)下开发(默认使用babel编译),因此也需要使用ES6开发小程序,ES6中有大量的语法糖可以让我们的代码更加简洁高效。 wepy继承了wx对象的方法,建议在wepy框架开发中不要用到wx对象的方法,虽然运行时效果是一样,但是打包时会cli报错(wepy中没有wx对象)wepy中组件中使用的是class,vue中使用的的是对象。 (2)数据绑定 小程序页面渲染层和JS逻辑层分开的,setData操作实际就是JS逻辑层与页面渲染层之间的通信,在同一次运行周期内多次执行setData操作时,通信的次数是一次还是多次取决于API本身的设计。WePY使用脏数据检查对setData进行封装,在函数运行周期结束时执行脏数据检查,一来可以不用关心页面多次setData是否会有性能上的问题,二来可以更加简洁去修改数据实现绑定,不用重复去写setData方法。 //原生小程序 this.setData({title: 'this is title'});//通过Page提供的setData方法来绑定数据 //wepy this.title = 'this is title'; //wepy 在异步函数中更新数据的时候,必须手动调用$apply方法,才会触发脏数据检查流程的运行 setTimeout(() => { this.title = 'this is title'; this.$apply(); }, 3000) //保留setData方法,但不建议使用setData执行绑定,修复传入undefined的bug,并且修改入参支持: this.setData(target, value) this.setData(object) (3)事件绑定以及传参优化 <view @tap="click"></view> //​​​​​ 原 bindtap="click"(省略了.default后缀 )绑定小程序冒泡型事件 <view @tap.stop="click"></view> //​​​​​ 原catchtap="click" 绑定小程序捕获型事件,如catchtap <view @tap.capture="click"></view> //​​​​​ 原 capture-bind:tap="click" <view @tap.capture.stop="click"></view> //​​​​​ 原 capture-catch:tap="click" <!--事件传参--> <view @tap="click({{index}})"></view> //​​​​​ 原bindtap="click" data-index={{index}} (4)框架默认对小程序提供的API全都进行了 Promise 处理,甚至可以直接使用async/await等新特性进行开发,同时修复了一些原生API的缺陷(如:wx.request的并发问题等) // 原生代码: wx.request({ url: 'xxx', success: function (data) { console.log(data); } }); // WePY 使用方式, 需要开启 Promise 支持,参考开发规范章节 wepy.request('xxxx').then((d) => console.log(d)); // async/await 的使用方式, 需要开启 Promise 和 async/await 支持,参考 WIKI async function request () { let d = await wepy.request('xxxxx'); console.log(d); } (5)computed 计算属性computed计算属性(类型:{ [key: string]: Function }),是一个有返回值的函数,可直接被当作绑定数据来使用,类似于data属性。需要注意的是,只要是组件中有任何数据发生了改变,那么所有计算属性就都会被重新计算。 data = { a: 1 } // 计算属性aPlus,在脚本中可通过this.aPlus来引用,在模板中可通过{{ aPlus }}来插值 computed = { aPlus () { return this.a + 1 } } (6)watcher 监听器 通过监听器watcher(类型:{ [key: string]: Function })能够监听到任何属性的更新。监听器适用于当属性改变时需要进行某些额外处理的情形。 data = { num: 1 } // 监听器函数名必须跟需要被监听的data对象中的属性num同名, // 其参数中的newValue为属性改变后的新值,oldValue为改变前的旧值 watch = { num (newValue, oldValue) { console.log(`num value: ${oldValue} -> ${newValue}`) } } // 每当被监听的属性num改变一次,对应的同名监听器函数num()就被自动调用执行一次 onLoad () { setInterval(() => { this.num++; this.$apply(); }, 1000) } (7)WXS (WeiXin Script) WePY 从1.7.x版本开始支持 wxs 语法,但语法与原生 wxs 稍有出入 a.wxs是基于原生的wxs去实现的,只是通过编译把现在的语法编译为原生语法 ②wxs必须是外链文件。并且后缀为.wxs ③wxs引入后只能在template中使用,不能在script中使用 /** project └── src ├── wxs | └── mywxs.wxs wxs 文件 ├── pages | └── index.wpy 页面 └──app.wpy **/ // mywxs.wxs module.exports = { text: 'This is from wxs', filter: function (num) { return num.toFixed(2); } }; // index.wpy <template> <text>{{m1.text}}</text> <text>{{m1.filter(num)}}</text> </template> <script> import wepy from 'wepy'; import mywxs from '../wxs/mywxs.wxs'; export default class Index extends wepy.page { data = { num: 10 }; wxs = { m1: mywxs } }; </script> (8)interceptor 拦截器 可以使用WePY提供的全局拦截器对原生API的请求进行拦截。具体方法是配置API的config、fail、success、complete回调函数。参考示例: import wepy from 'wepy'; export default class extends wepy.app { constructor () { // this is not allowed before super() super(); // 拦截request请求 this.intercept('request', { // 发出请求时的回调函数 config (p) { // 对所有request请求中的OBJECT参数对象统一附加时间戳属性 p.timestamp = +new Date(); console.log('config request: ', p); // 必须返回OBJECT参数对象,否则无法发送请求到服务端 return p; }, // 请求成功后的回调函数 success (p) { // 可以在这里对收到的响应数据对象进行加工处理 console.log('request success: ', p); // 必须返回响应数据对象,否则后续无法对响应数据进行处理 return p; }, //请求失败后的回调函数 fail (p) { console.log('request fail: ', p); // 必须返回响应数据对象,否则后续无法对响应数据进行处理 return p; }, // 请求完成时的回调函数(请求成功或失败都会被执行) complete (p) { console.log('request complete: ', p); } }); } } 三、WePY项目的目录结构 dist目录为WePY通过build指令生成的目录,除额外增加的npm目录外,其目录结构与原生小程序的目录结构类似。 原生小程序:app(app.js、app.json、app.wxss),page(page.js、page.json、page.wxml、page.wxss),文件必须同名。WePY中则使用了单文件模式:app.wpy,page.wpy。 ==> 一个.wpy文件可分为三大部分,各自对应于一个标签: 脚本<script>部分,又可分为两个部分: 逻辑部分,除了config对象之外的部分,对应于原生的.js文件 配置部分,即config对象,对应于原生的.json文件 结构<template>部分,对应于原生的.wxml文件 样式<style>部分,对应于原生的.wxss文件 <!--lang决定了其代码编译过程,src决定是否外联代码,存在src属性且有效时,会忽略内联代码--> <style lang="less" src="page1.less"></style>//lang值:css(默认)、less、scss、stylus、postcss <template lang="wxml" src="page1.wxml"></template>//lang值:wxml(默认)、xml、pug(原jade)。入口文件app.wpy不需要template,所以编译时会被忽略。 <script></script>//lang值:babel(默认、TypeScript 主要对目录的以下几点分析(其他目录文件详解类似于https://my.oschina.net/wangnian/blog/2050375中做的分析): 1、src文件夹 (1)components组件: 组件实例继承自wepy.component类,除了不需要config配置以及页面特有的一些生命周期函数之外,其属性与页面属性大致相同: import wepy from 'wepy'; export default class MyComponent extends wepy.component { props = {}//接收父组件传来的参数 customData = {} // 自定义数据 customFunction () {} //自定义方法 onLoad () {} // 在Page和Component共用的生命周期函数 data = {}; // 页面所需数据均需在这里声明,可用于模板数据绑定 components = {}; // 声明页面中所引用的组件,或声明组件中所引用的子组件 mixins = []; // 声明页面所引用的Mixin实例 computed = {}; // 声明计算属性(详见后文介绍) watch = {}; // 声明数据watcher(详见后文介绍) methods = {}; // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明,不能放在methods中,会报错 events = {}; // WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数 } /** 与page不同的是component不存在: onShow () {} // 只在Page中存在的页面生命周期函数 config = {}; // 只在Page实例中存在的配置数据,对应于原生的page.json文件,类似于app.wpy中的config onReady() {} // 只在Page中存在的页面生命周期函数 **/ 原生小程序支持js模块化,但彼此独立,业务代码与交互事件仍需在页面处理。无法实现组件化的松耦合与复用(如,模板A中绑定一个bindtap="myclick",模板B中同样绑定一样bindtap="myclick",那么就会影响同一个页面事件、数据) WePY组件的所有业务与功能在组件本身实现,组件与组件之间彼此隔离(上述例子在WePY的组件化开发过程中,A组件只会影响到A所绑定的myclick,B也如此) // 原生代码: <!-- item.wxml --> <template name="item"> <text>{{text}}</text> </template> <!-- index.wxml --> <import src="item.wxml"/> <template is="item" data="{{text: 'forbar'}}"/> <!-- index.js --> var item = require('item.js') // WePY <!-- /components/item.wpy --> <text>{{text}}</text> <!-- index.wpy --> <template> <com></com> </template> <script> import wepy from 'wepy'; import Item from '../components/item'; export default class Index extends wepy.page { components = { com: Item } } </script> ps,在 1.7.2-alpha4 的实验版本中提供了对原生组件的支持 a.引用组件 页面可以引入组件,而组件还可以引入子组件 /** project └── src ├── components | └── child.wpy ├── pages | ├── index.wpy index 页面配置、结构、样式、逻辑 | └── log.wpy log 页面配置、结构、样式、逻辑 └──app.wpy 小程序配置项(全局公共配置、公共样式、声明钩子等) **/ // index.wpy <template> <!-- 以`<script>`脚本部分中所声明的组件ID为名命名自定义标签,从而在`<template>`模板部分中插入组件 --> <view class="child1"> <child></child> </view> <view class="child2"> <!--注意:WePY中,在父组件template模板部分插入驼峰式命名的子组件标签时,不能将驼峰式命名转换成短横杆式命名(比如将childCom转换成child-com,这与Vue中不一致)--> <anotherchild></anotherchild> </view> </template> <script> import wepy from 'wepy'; //引入组件文件 import Child from '../components/child'; export default class Index extends wepy.component { //声明组件,分配组件id为child,需要注意的是,WePY中的组件都是静态组件,是以组件ID作为唯一标识的,每一个ID都对应一个组件实例,当页面引入两个相同ID的组件时,这两个组件共用同一个实例与数据,当其中一个组件数据变化时,另外一个也会一起变化。 components = { //为两个相同组件的不同实例分配不同的组件ID,从而避免数据同步变化的问题 child: Child, anotherchild: Child }; } </script> ②循环渲染组件(1.4.6新增) WePY 1.x 版本中,循环渲染WePY组件时(类似于通过wx:for循环渲染原生的wxml标签),必须使用WePY定义的辅助标签<repeat> WePY组件是静态编译组件(在编译阶段编译进页面),每个组件都是唯一的一个实例,目前只提供简单的repeat支持(不支持在repeat的组件中去使用props,computed,watch等),因此如下: <!-- 错误使用 ---> // list.wpy <view>{{test.name}}</view> // index.wpy <repeat for="{{mylist}}"> <List :test.sync="item"></List> </repeat> <!-- 推荐用法 ---> // list.wpy <repeat for="{{mylist}}"> <view>{{item.name}}</view> </repeat> // index.wpy <List :mylist.sync="mylist"></List> ③props 传值 props传值在WePY中属于父子组件之间传值的一种机制,包括静态传值与动态传值。 静态传值为父组件向子组件传递常量数据,因此只能传递String字符串类型。 动态传值是指父组件向子组件传递动态数据内容,父子组件数据完全独立互不干扰。但可以通过使用.sync修饰符来达到父组件数据绑定至子组件的效果,也可以通过设置子组件props的twoWay: true来达到子组件数据绑定至父组件的效果。如果既使用.sync修饰符,同时子组件props中添加的twoWay: true时,就可以实现数据的双向绑定。 // parent.wpy <!--在父组件template模板部分所插入的子组件标签中,使用:prop属性(等价于Vue中的v-bind:prop属性)来进行动态传值--> <child :title="parentTitle" :syncTitle.sync="parentTitle" :twoWayTitle="parentTitle"></child> <!--通过使用.sync修饰符来达到父组件数据绑定至子组件的效果--> <script> data = { parentTitle: 'p-title' }; </script> // child.wpy <script> props = { // 静态传值 title: String, // 父向子单向动态传值 syncTitle: { type: String, default: 'null' }, // 双向绑定 twoWayTitle: { type: String, default: 'nothing', twoWay: true//twoWay: true(默认false)来达到子组件数据绑定至父组件的效果,如果同时使用.sync修饰符,就可以实现双向绑定 } }; onLoad () { console.log(this.title); // p-title console.log(this.syncTitle); // p-title console.log(this.twoWayTitle); // p-title this.title = 'c-title'; console.log(this.$parent.parentTitle); // p-title. this.twoWayTitle = 'two-way-title'; this.$apply(); console.log(this.$parent.parentTitle); // two-way-title. --- twoWay为true时,子组件props中的属性值改变时,会同时改变父组件对应的值 this.$parent.parentTitle = 'p-title-changed'; this.$parent.$apply(); console.log(this.title); // 'c-title'; console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修饰符的props属性值,当在父组件中改变时,会同时改变子组件对应的值。 } </script> ④组件通信与交互 wepy.component基类提供$broadcast、$emit、$invoke三个方法用于组件之间的通信和交互: $broadcast $broadcast事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消。事件广播的顺序为广度优先搜索顺序 $emit $emit与$broadcast正好相反,组件发起事件后所有祖先组件会依次接收到$emit事件 //子组件: this.$emit('some-event', 1, 2, 3, 4); //父组件: import wepy from 'wepy' export default class Com extends wepy.component { components = {}; data = {}; methods = {}; // events对象中所声明的函数为用于监听组件之间的通信与交互事件的事件处理函数 events = { 'some-event': (p1, p2, p3, $event) => { console.log(`${this.$name} receive ${$event.name} from ${$event.source.$name}`); } }; // Other properties } $invoke $invoke是一个页面/组件对另一个组件中的方法的直接调用,通过传入组件路径找到相应的组件,然后再调用其方法。 this.$invoke('ComA', 'someMethod', 'someArgs');//在页面Page_Index中调用组件ComA的某个方法 this.$invoke('./../ComB/ComG', 'someMethod', 'someArgs');//在组件ComA中调用组件ComG的某个方法 //只能在methods中使用 在周期函数使用会报错 ⑤组件自定义事件处理函数(.user事件后缀,1.4.8新增) 示例如下(注意,如果用了自定义事件,则events中对应的监听函数不会再执行): // index.wpy <template> <child @childFn.user="parentFn"></child> </template> <script> import wepy from 'wepy' import Child from '../components/child' export default class Index extends wepy.page { components = { child: Child } methods = { parentFn (num, evt) { console.log('parent received emit event, number is: ' + num) } } } </script> // child.wpy <template> <view @tap="tap">Click me</view> </template> <script> import wepy from 'wepy' export default class Child extends wepy.component { methods = { tap () { console.log('child is clicked') this.$emit('childFn', 100) } } } </script> ⑥slot 组件内容分发插槽 WePY中的slot插槽作为内容分发标签的空间占位标签,便于在父组件中通过对相当于扩展板卡的内容分发标签的“插拔”,更为灵活、方便地对子组件进行内容分发。 注意,父组件中的标签必须有slot属性,且值为子组件中对应的slot名,这样父组件内容分发标签中的内容(即便没有内容,子组件插槽中的默认内容也不会显示出来,只有删除了父组件中对应的内容分发标签,才能显示出来)会覆盖掉子组件对应插槽中的默认内容 //首先在子组件template模板部分中声明slot标签作为内容插槽,同时必须在其name属性中指定插槽名称,还可设置默认的标签内容 <view class="panel"> <slot name="title">默认标题</slot> <slot name="content">默认内容</slot> </view> //然后在引入了该带有插槽的子组件的父组件template模板部分中声明用于“插拔”的内容分发标签 <panel> <view slot="title">新的标题</view> <view slot="content"> <text>新的内容</text> </view> </panel> eg:message-com.wpy: <template> <!-- 1.4.6新增:循环渲染时(类似于原生的wx:for),必须使用WePY定义的辅助标签"<repeat>" --> <repeat for="{{messageList}}" index="index" item="message"> <navigator url="/pages/topicDetail?topicId={{message.busiId}}"> <view class="msgList"> <!-- "<text selectable='true'>" 使文字可选--> <text selectable='true'>{{message.content}}</text> <view style="text-align: right;"> <text style="color: rgb(202,202,202);">{{message.createTime}}</text> </view> </view> </navigator> </repeat> </template> <script> import wepy from 'wepy' //引入wepy export default class Message extends wepy.component {//创建组件实例 props = {//接收父组件参数 messageList: {// * 类型和默认值参数不能省略,组件会偶发性接收不到父组件传递的参数 type: Array, default:[] } }; } </script> <style scoped lang="less"> .topicInfo text { font-size: 12px; color: #666; } .topicBottom view { margin-right: 15px; } .msgList { background-color: #fff; margin-top: 7px; padding: 10px 15px; } </style> message-page.wpy: <template> <view wx:if="{{!messageList.length}}" class="noData">暂无消息</view> <block wx:else> <message :messageList.sync="messageList"></message> <downLoad :show.sync="getListParam"></downLoad> </block> </template> <script> import wepy from 'wepy' import Message from '../components/message' import DownLoad from '../components/downLoad' import listMixin from '../mixins/list' import {connect} from 'wepy-redux' import {getMessageList} from '../store/actions' @connect({ messageList(state) { return state.reducer.messageList; } }, { getMessageList }) export default class MineMessages extends wepy.page { config = { navigationBarTitleText: '我的消息' }; components = { message: Message, downLoad: DownLoad }; data = {}; mixins = [listMixin]; methods = { getList(params) { let listParam = { messageType: 4 }; if (wx.getStorageSync('userId')) { this.getMessageList(Object.assign(listParam, params)); } else { wx.showToast({ title: '您还没有登录', icon: 'none' }); } } }; } </script> (2)mixins:Mixin 混合(公用的js) 混合可以将组件之间的可复用部分抽离,从而在组件中使用混合时,可以将混合的数据,事件以及方法注入到组件之中。混合分为两种: 默认式混合(data、components、events、自定义方法),即组件未声明的选项将混合对象中注入组件之中,组件已声明的选项将不受影响 兼容式混合(methods响应事件 、小程序页面事件),即先响应组件本身响应事件,然后再响应混合对象中响应事件(Vue则相反,先执行mixin中的函数, 再执行组件本身的函数) eg:listMixin.js: import wepy from 'wepy' export default class ListMixin extends wepy.mixin {//创建mixin实例 config = { enablePullDownRefresh: true,//开启下拉刷新,默认是关闭的 onReachBottomDistance: 10,//设置触发下拉刷新的底部距离 backgroundColor: "#eab010"//设置背景色 }; data = { getListParam: {//获取列表数据的通用参数 hasMoreData: true, loading: false, noMore: false, page: 1, limit: 10, } }; /** * 页面相关事件处理函数--监听用户下拉动作(下拉刷新) */ onPullDownRefresh() { wx.showNavigationBarLoading(); //在标题栏中显示加载 this.data.getListParam.page = 1;//下拉刷新时参数page=1 this.methods.getList(this.data.getListParam);//调用组件中的获取列表函数 setTimeout(() => { wx.hideNavigationBarLoading(); //完成停止加载 wx.stopPullDownRefresh(); //停止下拉刷新 }, 300) }; /** * 页面上拉触底事件的处理函数(上拉加载) */ onReachBottom() { if (this.data.getListParam.hasMoreData) { this.data.getListParam.page++;//每触发一次page++ this.data.getListParam.hasMoreData = false;//关闭可调用函数 this.data.getListParam.loading = true;//显示加载中...(自己写的底部加载中样式的组件) this.methods.getList(this.data.getListParam);//调用组件中的获取列表函数 setTimeout(() => { this.data.getListParam.loading = false;//关闭显示加载中...(自己写的底部加载中样式的组件) this.$apply();//强制渲染 }, 100); } else { this.data.getListParam.noMore = true;//显示加载完成(自己写的底部加载到底样式的组件) } }; onReady() { this.methods.getList(this.data.getListParam);//初始化请求页面数据 } } 使用时引入然后注入到组件实例(最好放在data属性后,否则会偶发性报错),eg: (3)页面page.wpy Page页面实际上继承自Component组件,即Page也是组件。除扩展了页面所特有的config配置以及特有的页面生命周期函数之外,其它属性和方法与Component一致: import wepy from 'wepy'; export default class MyPage extends wepy.page { customData = {} // 自定义数据 customFunction () {} //自定义方法 onLoad () {} // 在Page和Component共用的生命周期函数 onUnload() {} // 监听页面卸载 onReady() {} // 只在Page中存在的页面生命周期函数 onShow () {} // 只在Page中存在的页面生命周期函数,当小程序启动,或从后台进入前台显示 onHide() {} // 只在Page中存在的页面生命周期函数,当小程序从前台进入后台 config = {}; // 只在Page实例中存在的配置数据,对应于原生的page.json文件,类似于app.wpy中的config data = {}; // 页面所需数据均需在这里声明,可用于模板数据绑定 components = {}; // 声明页面中所引用的组件,或声明组件中所引用的子组件 mixins = []; // 声明页面所引用的Mixin实例 computed = {}; // 声明计算属性(详见后文介绍) watch = {}; // 声明数据watcher(详见后文介绍) methods = {}; // 声明页面wxml中标签的事件处理函数。注意,此处只用于声明页面wxml中标签的bind、catch事件,自定义方法需以自定义方法的方式声明 events = {}; // WePY组件事件处理函数对象,存放响应组件之间通过$broadcast、$emit、$invoke所传递的事件的函数 onPullDownRefresh(){} // 监听用户下拉动作 onReachBottom(){} // 页面上拉触底事件的处理函数 onShareAppMessage(){} // 用户点击右上角分享 onPageScroll(){} // 页面滚动 onTabItemTap(){} // 当前是 tab 页时,点击 tab 时触发 } (4)状态管理store(redux) (5)app.wpy:小程序入口 app.wpy实例继承自wepy.app类,包含:config配置对象(对应原生的app.json文件,build编译时会根据config属性自动生成app.json文件)、globalData全局数据对象、自定义方法与属性、小程序生命周期函数。 在Page页面实例中,可以通过this.$parent来访问App实例。 <script> import wepy from 'wepy'; export default class extends wepy.app { config = { pages:[// pages定义当前小程序所有页面路径,让微信客户端知道当前你的小程序页面定义在哪个目录 'pages/index', 'pages/mine' ], window:{//window定义小程序所有页面的顶部背景颜色,文字颜色定义等。 "backgroundTextStyle": "light", "navigationBarBackgroundColor": "#fff", "navigationBarTitleText": "顶部标题", "navigationBarTextStyle": "black" }, tabBar: { selectedColor: '#ea9100',//底部导航字体选中时的颜色 list: [ { pagePath: 'pages/index',//导航页面路径(必须在pages中定义) text: '首页',//导航名 iconPath: 'img/index.png',//未选中的导航icon selectedIconPath: 'img/index1.png'//选中时的导航icon }, { pagePath: 'pages/mine', text: '我的', iconPath: 'img/mine.png', selectedIconPath: 'img/mine1.png' } ] }, }; onLaunch() {//初始化 console.log(this); } onShow(){}//当小程序启动,或从后台进入前台显示 onHide(){}//当小程序从前台进入后台 } </script> <style lang="less"> /** less **/ </style> (6)index.template.html:web页面的入口文件 2、project.config.json小程序项目配置文件 1.7.0 之后的版本会在根目录包含project.config.json,使用微信开发者工具-->添加项目,项目目录请选择项目根目录即可根据配置完成项目信息自动配置。 1.7.0 之前生成的代码包可能不存在project.config.json文件,建议手动创建该文件后再添加项目。project.config.json文件内容如下: { "description": "project description", "setting": { "urlCheck": true,//对应不检查安全域名选项,开启。 如果已配置好安全域名则建议关闭 "es6": false,//对应关闭ES6转ES5选项,关闭。 重要:未关闭会运行报错 "postcss": false,//对应关闭上传代码时样式自动补全选项,关闭。 重要:某些情况下漏掉此项也会运行报错 "minified": false //对应关闭代码压缩上传选项,关闭。重要:开启后,会导致真机computed, props.sync 等等属性失效。(注:压缩功能可使用WePY提供的build指令代替) }, "compileType": "miniprogram", "appid": "touristappid", "projectname": "Project name", "miniprogramRoot": "./dist" } 3、wepy.config.js const path = require('path'); var prod = process.env.NODE_ENV === 'production'; module.exports = { /*wpyExt:缺省值为'.wpy',IDE默认情况下不会对此文件类型进行高亮处理 除了进行前文代码高亮设置之外 还可以直接将相关文件的后缀名由.wpy修改为.vue,然后将此选项修改为.vue */ wpyExt: '.wpy', eslint: false, cliLogs: !prod, build: { web: { htmlTemplate: path.join('src', 'index.template.html'), htmlOutput: path.join('web', 'index.html'), jsOutput: path.join('web', 'index.js') } }, resolve: { alias: { counter: path.join(__dirname, 'src/components/counter'), '@': path.join(__dirname, 'src') }, aliasFields: ['wepy', 'weapp'], modules: ['node_modules'] }, /** compilers为1.3.1版本之后的功能,如果需要使用其它语法,请先配置compilers,然后再安装相应的compilers 目前支持wepy-compiler-less, wepy-compiler-postcss,wepy-compiler-sass、wepy-compiler-babel、wepy-compiler-pug 其他compiler持续开发中 */ compilers: { less: { compress: prod }, /*sass: { outputStyle: 'compressed' },*/ babel: { sourceMap: true, presets: [ 'env' ], /* plugins为1.1.6版本之后的功能 目前支持js压缩wepy-plugin-ugliyjs、图片压缩wepy-plugin-imagemin 其他plugin持续开发中 */ plugins: [ 'transform-class-properties', 'transform-decorators-legacy', 'transform-object-rest-spread', 'transform-export-extensions', ] } }, plugins: {}, /*在使用的地方先引入import wepy from 'wepy'; 然后wepy.$appConfig.属性名。eg:wepy.$appConfig.baseUrl*/ appConfig: { baseUrl:process.env.NODE_ENV === 'production'? 'https://api.a.com/' : 'https://api.a.com/dev/',// 运行 npm run dev命令时,域名为https://api.a.com/dev/,运行 npm run build时域名为https://api.a.com/ noPromiseAPI: ['createSelectorQuery'] } } if (prod) { // 压缩sass module.exports.compilers['less'] = {outputStyle: 'compressed'} // 压缩js module.exports.plugins = { uglifyjs: { filter: /\.js$/, config: {} }, imagemin: { filter: /\.(jpg|png|jpeg)$/, config: { jpg: { quality: 80 }, png: { quality: 80 } } } } } 相关链接:mpvue文档、原生小程序文档、公众号文档 博客地址:https://my.oschina.net/wangnian

优秀的个人博客,低调大师

快速了解 前端打包 webpack

一、介绍 (1)概念 webpack 用于编译 JavaScript 模块,是JavaScript 静态模块的打包工具。从 webpack v4.0.0 开始,可以不用引入一个配置文件。 (2)核心 1.入口(entry):指定webpack打包编译从哪个文件开始下手 入口起点(entry point)指示 webpack 使用哪个模块,作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。 webpack.config.js: module.exports = { entry: { main: './src' //打包入口,来指定一个入口起点(或多个入口起点,默认值为 ./src) }, entry: './src', //这个是上面的简写方式,是等价的 entry: { home: "./home.js", about: "./about.js", contact: "./contact.js" },//对象法指定多个入口,如果你想要多个依赖一起注入到一个模块,向 entry 属性传入「文件路径(file path)数组」。 entry: () => new Promise((resolve) => resolve(['./demo', './demo2'])),//动态入口,当结合 output.library 选项时:如果传入数组,则只导出最后一项 }; 2.出口(output):指定webpack打包编译后的路径及文件名 output属性告诉 webpack 在哪里输出它所创建的bundles,以及如何命名这些文件。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。 webpack.config.js: const path = require('path'); module.exports = { entry: './src',//配置入口路径 output: { path: path.resolve(__dirname, 'dist'),//打包文件夹名,默认值为./dist filename: '[name].js'//入口文件名 } }; 3.loader(加载器):webpack识别不了的语言通过加载器来翻译 loader 用于转换某些类型的模块,webpack 自身只理解 JavaScript,loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块。loader 能够import导入任何类型的模块(如.css),是 webpack 特有的功能,其他打包工具有可能不支持。 webpack.config.js: const path = require('path'); const config = { output: { filename: 'my-first-webpack.bundle.js' }, module: { rules: [//在 webpack 配置中定义 loader 时,要定义在module.rules中,里面包含两个"必须属性":test和use { test: /\.txt$/, //test定义需要使用相应 loader 进行转换的某类文件 use: 'raw-loader' //use定义使用哪个 loader 进行转换 } ] } }; module.exports = config; 常见的例子: 4.插件(plugins):webpack完成不了的功能通过插件来完成 插件接口功能极其强大,可以用来处理各种各样的任务。通过require()使用插件,然后把它添加到plugins数组中。多数插件可以通过选项(option)自定义。 webpack.config.js: const HtmlWebpackPlugin = require('html-webpack-plugin'); // 通过 npm 安装 const webpack = require('webpack'); // 用于访问内置插件 const config = { module: { rules: [ { test: /\.txt$/, use: 'raw-loader' } ] }, plugins: [ //在一个配置文件中因为不同目的而多次使用同一个插件,需要通过使用new操作符来创建它的一个实例 new webpack.optimize.UglifyJsPlugin(), new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config; 5.模式(mode):通过模式确定是开发环境还是生产环境以便加载不同配置 module.exports = { mode: 'production'//通过选择development(开发)或production(生产),启用相应模式下的 webpack 内置的优化 }; 二、安装 (1)安装nodeJs webpack 配置是标准的 Node.js CommonJS 模块,在安装webpack之前,请确保安装了Node.js的最新版本,使用旧版本可能遇到各种问题(可能缺少 webpack 功能或者缺少相关 package 包)。 (2)安装webpack 1.本地安装 对于大多数项目,我们建议本地安装。这可以使我们在引入破坏式变更(breaking change)的依赖时,更容易分别升级项目。 npm install --save-dev webpack //安装最新版本 npm install --save-dev webpack@<version> //安装特定版本 操作如下: 2.全局安装 不推荐全局安装 webpack。这会将你项目中的 webpack 锁定到指定版本,并且在使用不同的 webpack 版本的项目中,可能会导致构建失败。 npm install --global webpack 3.最新体验版本 最新体验版本(可能仍然包含 bug)不应该用于生产环境 //直接从 webpack 的仓库中安装 npm install webpack@beta npm install webpack/webpack#<tagname/branchname> 4.安装 CLI 如果你使用 webpack 4+ 版本,你还需要安装 CLI。 npm install --save-dev webpack-cli //webpack-cli用于在命令行中运行 webpack 操作如下: 三、配置(configuration) webpack 的配置文件,是导出一个对象的 JavaScript 文件。webpack 需要传入一个配置对象(configuration object), 根据对象定义的属性进行解析,因此很少有 webpack 配置看起来很完全相同。可以通过两种方式(终端、Node.js)使用 webpack。 webpack 配置是标准的 Node.js CommonJS 模块,你可以: 通过require(...)导入其他文件,使用 npm 的工具函数 使用 JavaScript 控制流表达式 对常用值使用常量或变量 编写并执行函数来生成部分配置 请在合适的时机使用这些特性,虽然技术上可行,但应避免以下做法: 在使用 webpack 命令行接口(CLI)(应该编写自己的命令行接口(CLI),或使用--env)时,访问命令行接口(CLI)参数 导出不确定的值(调用 webpack 两次应该产生同样的输出文件) 编写很长的配置(应该将配置拆分为多个文件) 你需要从这份文档中收获最大的点,就是你的 webpack 配置,可以有很多种的格式和风格。但为了你和你的团队能够易于理解和维护,你们要始终采取同一种用法、格式和风格。 基本配置 webpack.config.js const HtmlWebpackPlugin = require('html-webpack-plugin'); //通过 npm 安装 const webpack = require('webpack'); //访问内置的插件 const path = require('path'); const config = { entry: './path/to/my/entry/file.js', output: { filename: 'my-first-webpack.bundle.js', path: path.resolve(__dirname, 'dist') }, module: { rules: [ { test: /\.(js|jsx)$/, use: 'babel-loader' } ] }, plugins: [ new webpack.optimize.UglifyJsPlugin(), new HtmlWebpackPlugin({template: './src/index.html'}) ] }; module.exports = config; 也可以 多个 Target:导出多个配置,或者,使用其他配置语言,webpack 接受以多种编程和数据语言编写的配置文件。

优秀的个人博客,低调大师

Vuex 快速入门 简单易懂

一、vuex介绍 (1)vuex是什么? 1. 借鉴了Flux、Redux、The Elm Architecture 2. 专为 Vue.js 设计的状态管理模式 3. 集中式存储和管理应用程序中所有组件的状态 4. Vuex 也集成到 Vue 的官方调试工具 5. 一个 Vuex 应用的核心是 store(仓库,一个容器),store包含着你的应用中大部分的状态 (state)。 (2)什么情况下我应该使用 Vuex? 1. 不适用:小型简单应用,用 Vuex 是繁琐冗余的,更适合使用简单的store 模式。 2. 适用于:中大型单页应用,你可能会考虑如何把组件的共享状态抽取出来,以一个全局单例模式管理,不管在哪个组件,都能获取状态/触发行为,解决问题如下: ① 多个视图使用于同一状态: 传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力 ② 不同视图需要变更同一状态: 采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝,通常会导致无法维护的代码 (3)Vuex 和单纯的全局对象有何不同? 1.Vuex 的状态存储是响应式的 当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。 2.你不能直接改变 store 中的状态 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation,方便我们跟踪每一个状态的变化。 二、vuex安装 * vue cli 3 中搭建脚手架预设时选择了“vuex”后便安装了vuex,可跳过此步阅读 (1)<script>引入 在 Vue 之后引入vuex会进行自动安装: <script src="/path/to/vue.js"></script> <script src="/path/to/vuex.js"></script> (2) 包管理 npm install vuex --save //yarn add vuex 在一个模块化的打包系统中,您必须显式地通过 Vue.use() 来安装 Vuex: import Vue from 'vue' import Vuex from 'vuex' Vue.use(Vuex) (3)git clone 自己构建 git clone https://github.com/vuejs/vuex.git node_modules/vuex cd node_modules/vuex npm install npm run build (4)兼容 Vuex 依赖Promise。如果你支持的浏览器并没有实现 Promise (如 IE),那么你可以使用一个 polyfill 的库(如 es6-promis) 1.你可以通过 CDN 将其引入,window.Promise会自动可用: <script src="https://cdn.jsdelivr.net/npm/es6-promise@4/dist/es6-promise.auto.js"></script> 2.包管理器安装: npm install es6-promise --save //yarn add es6-promise 然后,将下列代码添加到你使用 Vuex 之前的一个地方: import 'es6-promise/auto' 三、使用 (1)使用介绍 1.搭建store实例 ①. 在创建vuex实例的地方引入vue、vuex,使用vuex: import Vue from 'vue'//引入vue import Vuex from 'vuex'//引入vuex Vue.use(Vuex); //使用 vuex ②. 如果你的actions中用到 store.dispath() ,要引入store import store from './store' //引入状态管理 store ③. new 一个 Vuex.Store 实例,并注册 state、mutations、actions、getters到Vuex.Store 实例中 ps. 你可以像上面那样将“属性和值”直接写在实例中,当代码量大时,你也可以分别写个.js文件,如下图: 然后引入到 store/index.js 注册到vuex实例中,如: 2.创建好 vuex.store 后,你需要在入口文件 main.js 中引入 store 并注册到 vue 实例中,这样就可以在任何组件使用store了: 3.在组件中使用: ①. 引入vuex中各属性对应的辅助函数: import {mapActions, mapState,mapGetters} from 'vuex' //注册 action 、 state 、getter ②. 使用store中的状态数据、方法: 使用方法有很多,这种事最简单实用的,更多可以查看官网学习:https://vuex.vuejs.org/zh/ (2)具体demo 来个简单易懂的计数 eg:store.js import Vue from 'vue'; import Vuex from 'vuex'; //引入 vuex import store from './store' //注册store Vue.use(Vuex); //使用 vuex export default new Vuex.Store({ state: { // 初始化状态 count: 0, someLists:[] }, mutations: { // 处理状态 increment(state, payload) { state.count += payload.step || 1; } }, actions: { // 提交改变后的状态 increment(context, param) { context.state.count += param.step; context.commit('increment', context.state.count)//提交改变后的state.count值 }, incrementStep({state, commit, rootState}) { if (rootState.count < 100) { store.dispatch('increment', {//调用increment()方法 step: 10 }) } } }, getters: { //处理列表项 someLists: state =>param=> { return state.someLists.filter(() => param.done) } } }) 使用时,eg: main.js: import Vue from 'vue' import App from './App.vue' import router from './router' import store from './store' //引入状态管理 store Vue.config.productionTip = false new Vue({ router, store,//注册store(这可以把 store 的实例注入所有的子组件) render: h => h(App) }).$mount('#app') views/home.vue: <template> <div class="home"> <!--在前端HTML页面中使用 count--> <HelloWorld :msg="count"/> <!--表单处理 双向绑定 count--> <input :value="count" @input="incrementStep"> </div> </template> <script> import HelloWorld from '@/components/HelloWorld.vue' import {mapActions, mapState,mapGetters} from 'vuex' //注册 action 和 state export default { name: 'home', computed: { //在这里映射 store.state.count,使用方法和 computed 里的其他属性一样 ...mapState([ 'count' ]), count () { return store.state.count } }, created() { this.incrementStep(); }, methods: { //在这里引入 action 里的方法,使用方法和 methods 里的其他方法一样 ...mapActions([ 'incrementStep' ]), // 使用对象展开运算符将 getter 混入 computed 对象中 ...mapGetters([ 'someLists' // ... ]) }, components: { HelloWorld } } </script> 运行结果: 博客地址:https://my.oschina.net/wangnian

优秀的个人博客,低调大师

PHP学习1——快速入门

主要内容: 搭建PHP开发环境 第一个helloworld程序 PHP(Hypertext Preprocessor)PHP超文本预处理程序,是一种嵌入HTML的脚本语言,运行在服务器。 搭建PHP开发环境 PHP开发环境主要3部分:服务器Apache,编程语言PHP,数据库MySQL。搭建开发环境,直接使用XAMPP集成安装就可以。 XAMPP=Apache+MySQL+PHP+Perl,X表示的是跨平台。 官网下载安装即可:https://www.apachefriends.org/zh_cn/index.html (安装太简单了,没有什么可说的,不过如果以前安装过tomcat,apache,MySQL等,xampp的部分功能可能不能使用,解决办法网上很多,不再赘言) 第一个helloworld程序 php可以使用note++进行编辑。 helloworld.php <?php echo "hello world!"; ?> 然后运行XAMPP的控制面板,点击Apache的start,绿色就是正常运行了。 然后将helloworld.php文件放在路径:C:\xampp\htdocs 浏览器输入:http://localhost/helloworld.php ok,我们的第一个PHP程序就完成了。

优秀的个人博客,低调大师

Dart In Action -Dart快速入门(-)

写在前面 本文基本上是将dart官网部分内容进行翻译,没兴趣的请出门左转至Dart的官网,有兴趣的同志请继续阅读本文。Flutter教程在这里 1.什么是Dart? 以下来自百度百科: Dart是谷歌开发的计算机编程语言,后来被Ecma (ECMA-408)认定为标准 [1] 。它被用于web、服务器、移动应用 [2] 和物联网等领域的开发。它是宽松开源许可证(修改的BSD证书)下的开源软件。 Dart是面向对象的、类定义的、单继承的语言。它的语法类似C语言,可以转译为JavaScript,支持接口(interfaces)、混入(mixins)、抽象类(abstract classes)、具体化泛型(reified generics)、可选类型(optional typing)和sound type system。 事实上,Dart被抛弃过,但Dart这门语言已经发展到2.x了。 2.为什么要学Dart? 大千世界,语言不计其数,这次我们要学习的为是Dart?答案是因为google推出的全新的跨平台UI框架--Flutter,而Flutter所使用的语言便是Dart。 3.初识Dart 以下代码展示了使用频率非常高并且最基础的Dart语法: // 定义一个函数. printInteger(int aNumber) { print('The number is $aNumber.'); // 将内容输出至控制台. } //和很多语言一样,dart选择了main()作为程序入口. main() { var number = 42; // 声明并且定义一个变量. printInteger(number); // 调用一个函数. } 4.Dart的注释 Dart的注释分为三种,举例说明: 单行注释 以//以为开始。在//和行尾之前的内容会被Dart编译器忽略掉。 void main() { // TODO: refactor into an AbstractLlamaGreetingFactory? print('Welcome to my Llama farm!'); } 多行注释 以/*开始并以*/为结束。/*和*/之前的内容会被Dart编译器忽略掉(除非注释是文档注释,下小节会介绍文档注释)。多行注释是可以嵌套的。 void main() { /* * This is a lot of work. Consider raising chickens. Llama larry = Llama(); larry.feed(); larry.exercise(); larry.clean(); */ } 3. 文档注释 文档注释是多行或者单行注释,以///或者/**开始。使用///注释连续的行与多行文档注释有相同的效果。 在文档注释中,Dart编译器会忽略所有文本,除非它被括在括号中。使用方括号,可以引用类、方法、字段、顶级变量、函数和参数。括号中的名称在文档化的程序元素的词法范围中解析。 /// A domesticated South American camelid (Lama glama). /// /// Andean cultures have used llamas as meat and pack /// animals since pre-Hispanic times. class Llama { String name; /// Feeds your llama [Food]. /// /// The typical llama eats one bale of hay per week. void feed(Food food) { // ... } /// Exercises your llama with an [activity] for /// [timeLimit] minutes. void exercise(Activity activity, int timeLimit) { // ... } } 在生成的文档中,[Food]成为指向Food类的API文档的链接。使用这个SDK可以解析Dart代码以及生HTML文档。 第一节结束

优秀的个人博客,低调大师

OpenStack快速入门-queens版本

系统内存要求8G,硬盘100G,操作系统可以连接公网,单网卡即可满足操作。本次实验使用了2块网卡。 操作系统版本:(最小化安装)CentOS Linux release 7.4.1708 (Core)openstack-queens版本centos-release-openstack-queens-1-1.el7.centos.x86_64 1.系统配置:关闭不需要服务:setenforce 0sed -i 's/SELINUX=enforcing/SELINUX=disabled/' /etc/selinux/configsystemctl disable firewalld systemctl stop firewalld systemctl disable NetworkManager systemctl stop NetworkManager 2.OpenStack软件安装:yum install centos-release-openstack-queens.x86_64 -y(安装openstack的yum 源)yum install -y openstack-packstackyum install python-pip -ypackstack --allinone (本次安装环境所有组建部署在一个节点。) 3,登陆dashboardhttp://192.168.0.78密码记录在keystonerc_admin文件中: unset OS_SERVICE_TOKEN export OS_USERNAME=admin export OS_PASSWORD=a8e90bb181da420e export OS_AUTH_URL=http://192.168.0.79:5000/v2.0 export PS1='[\u@\h \W(keystone_admin)]\$ ' export OS_TENANT_NAME=admin export OS_REGION_NAME=RegionOne

优秀的个人博客,低调大师

Docker 快速入门之 Dockerfile

在容器环境中,我们可以通过容器创建一个我们自定义过的镜像,那么我们是否可以直接通过基础的镜像直接自定义镜像呢?答案当然是可以的,在 Docker 中我们可以从名为Dockerfile的文件中读取指令并且自动构建镜像。在本文中,将介绍 Dockerfile 的基本语法以及基本知识。 1 Dockerfile 是什么? Dockerfile 其实是一份文本文档,里面包含了用户可以用来操作镜像的一些指令。通过顺序执行这些指令,最后得到一个自定义的镜像,这有点类似于我们的shell script。 2 Dockerfile 示例 接下来先看一个 Dockerfile 示例: FROM centos LABEL maintainer="Locez <locez@locez.com>" ENV TEST="This is a test env" COPY nginx.repo /etc/yum.repos.d/nginx.repo RUN yum update -y && \ yum install -y nginx COPY nginx.conf /etc/nginx/nginx.conf COPY index.html /usr/share/nginx/html/index.html COPY index_files/ /usr/share/nginx/html/index_files/ EXPOSE 80 CMD ["/usr/sbin/nginx","-g","daemon off;"] 在上面我们可以看到 Dockerfile 中的一些指令,通过名称我们也可以猜到这些指令大概是干嘛的,其中有一些对文件的操作,因此我们先来看看用于存放 Dockerfile 的这个目录的目录结构: # tree . . ├── Dockerfile ├── index_files │ ├── 145049z4og8xyjhx4xy8go.jpg │ ├── 222746e5vh38d7ey3leyps.jpg │ ├── 88x31.png │ ├── archlinux-splash.png │ ├── bdshare.css │ ├── Best-Linux-Markdown-Editors.png │ ├── core.js │ ├── docker-icon.jpg │ ├── hadoop-pic1.png │ ├── jquery_002.js │ ├── jquery.css │ ├── jquery.js │ ├── MathJax.js │ ├── pic.gif │ ├── raspberrypiraspberry-pi-logo.jpg │ ├── script.js │ ├── scrollup.png │ ├── share.js │ ├── style.css │ └── z_stat.js ├── index.html ├── nginx.conf └── nginx.repo 1 directory, 24 files 构建镜像 在当前目录下执行以下命令构建镜像: # docker build -t locez/nginx . Sending build context to Docker daemon 1.851 MB Step 1/10 : FROM centos ---> 196e0ce0c9fb Step 2/10 : LABEL maintainer "Locez <locez@locez.com>" ---> Using cache ---> 9bba3042bcdb Step 3/10 : ENV TEST "This is a test env" ---> Using cache ---> c0ffe95ea0c5 Step 4/10 : COPY nginx.repo /etc/yum.repos.d/nginx.repo ---> Using cache ---> bb6ee4c30d56 Step 5/10 : RUN yum update -y && yum install -y nginx ---> Using cache ---> 6d46b41099c3 Step 6/10 : COPY nginx.conf /etc/nginx/nginx.conf ---> Using cache ---> cfe908390aae Step 7/10 : COPY index.html /usr/share/nginx/html/index.html ---> Using cache ---> 21729476079d Step 8/10 : COPY index_files/ /usr/share/nginx/html/index_files/ ---> Using cache ---> 662f06ec7b46 Step 9/10 : EXPOSE 80 ---> Using cache ---> 30db5a889d0a Step 10/10 : CMD /usr/sbin/nginx -g daemon off; ---> Using cache ---> d29b9d4036d2 Successfully built d29b9d4036d2 然后用该镜像启动容器: # docker run -d -it --rm --name test-nginx -p 8080:80 locez/nginx e06fd991ca1b202e08cf1578f8046355fcbba10dd9a90e11d43282f3a1e36d29 用浏览器访问http://localhost:8080/即可看到部署的内容。 3 Dockerfile 指令解释 Dockerfile 支持FROM,RUN,CMD,LABEL,EXPOSE,ENV,ADD,COPY,ENTRYPOINT,VOLUME,USER,WORKDIR,ARG,ONBUILD,SHELL等指令,这里只选择常用的几个进行讲解,可结合上面的示例进行理解。其它的请自行查阅官方文档。 3.1 FROM FROM指令用于指定要操作的基础镜像,因为在我们构建我们自己的镜像的时候需要一个基础镜像。 语法: FROM <image> [AS <name>] FROM <image>[:<tag>] [AS <name>] 其中[AS <name>]为指定一个名称,在一个 Dockerfile 中多次使用FROM时如有需要,可用COPY --from=<name|index>语法进行复制。 3.2 RUN RUN指令用于执行命令,并且是在新的一层上执行,并把执行后的结果提交,也就是生成新的一层。基于这个问题,我们在使用RUN指令时应该尽可能的把要执行的命令一次写完,以减少最后生成的镜像的层数。语法: RUN <command> RUN ["executable", "param1", "param2"] 3.3 CMD CMD指令用于给容器启动时指定一个用于执行的命令,例如上例中的 nginx 启动命令。 语法: CMD ["executable","param1","param2"] CMD ["param1","param2"] ### 用于给 ENTRYPOINT 指令提供默认参数 CMD command param1 param2 3.4 LABEL LABEL指令用于为镜像指定标签,可用docker inspect命令查看。可用来代替被舍弃的MAINTAINER命令。语法: LABEL <key>=<value> <key>=<value> <key>=<value> ... 3.5 EXPOSE EXPOSE指令用于告诉 Docker 容器监听的特殊端口,但是此时端口还没有暴露给 host ,只有当在运行一个容器显式用参数-p或者-P的时候才会暴露端口。语法: EXPOSE <port> [<port>/<protocol>...] 3.6 ENV ENV指令用于设定环境变量。语法: ENV <key> <value> ENV <key>=<value> ... 3.7 ADD ADD指令用于复制新文件,目录,远程文件到容器中。其中<src>可以为文件,目录,URL,若为可解压文件,在复制后会解压。语法: ADD <src>... <dest> ADD ["<src>",... "<dest>"] 3.8 COPY COPY指令与ADD指令非常相似,但COPY比较直观且简单,它只支持本地的文件以及目录的复制,不像ADD指令可以远程获取文件并解压。语法: COPY <src>... <dest> COPY ["<src>",... "<dest>"] 3.9 ENTRYPOINT ENTRYPOINT指令也跟CMD指令相似,用于指定容器启动时执行的命令。当使用ENTRYPOINT指令时,可用CMD命令配合,这样在启动容器时,可以对CMD指令写入的参数进行覆盖。语法: ENTRYPOINT ["executable", "param1", "param2"] 样例: ENTRYPOINT ["top","-b"] CMD ["-c"] 上面的-c参数可以在启动时覆盖docker run -it --rm --name test top -H。 如果要覆盖ENTRYPOINT指令则用--entrypoint参数启动容器。 3.10 VOLUME VOLUME指令用于为容器创建一个挂载点,这个挂载点可以用来挂载本地文件/文件夹也可以用来挂载数据卷。其中若在启动一个新容器时没有指定挂载目录,则会自动创建一个数据卷,当容器被销毁时,数据卷如果没有被其它容器引用则会被删除。语法: VOLUME ["/data1","/data2"] 3.11 USER USER指令用于设置执行RUN,CMD,ENTRYPOINT等指令的用户以及用户组。默认为root用户。语法: USER <user>[:<group>] 3.12 WORKDIR WORKDIR指令用于设置RUN,CMD,ENTRYPOINT,COPY,ADD等指令的工作目录。语法: WORKDIR /path/to/workdir 4 总结 本文从一个具体的例子出发,讲述了如何利用 Dockerfile 构建镜像,然后解释了 Dockerfile 文件中的指令的语法,有关更多内容可访问官方文档。 5 参考资料 Dockerfile reference 原文发布时间: 2017-12-16 本文来自云栖社区合作伙伴“ Debian社区”,了解相关信息可以关注“ Debian社区”。

优秀的个人博客,低调大师

Mina2.0快速入门

MinaTimeServer.java 复制代码 package com.vista; import java.io.IOException; import java.net.InetSocketAddress; import java.nio.charset.Charset; import org.apache.mina.core.service.IoAcceptor; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.filter.codec.ProtocolCodecFilter; import org.apache.mina.filter.codec.textline.TextLineCodecFactory; import org.apache.mina.filter.logging.LoggingFilter; import org.apache.mina.transport.socket.nio.NioSocketAcceptor; public class MinaTimeServer { private static final int PORT = 6488; public static void main(String[] args) throws IOException { //监听即将到来的TCP连接 IoAcceptor acceptor = new NioSocketAcceptor(); acceptor.getFilterChain().addLast("logger", new LoggingFilter()); acceptor.getFilterChain().addLast("codec", new ProtocolCodecFilter( new TextLineCodecFactory( Charset.forName("UTF-8")))); acceptor.setHandler(new TimeServerHandler()); acceptor.getSessionConfig().setReadBufferSize(2048); acceptor.getSessionConfig().setIdleTime(IdleStatus.BOTH_IDLE, 10); acceptor.bind(new InetSocketAddress(PORT)); System.out.println("服务器启动"); } } 复制代码 TimeServerHandler.java 复制代码 package com.vista; import java.util.Date; import org.apache.mina.core.service.IoHandlerAdapter; import org.apache.mina.core.session.IdleStatus; import org.apache.mina.core.session.IoSession; public class TimeServerHandler extends IoHandlerAdapter { public void exceptionCaught(IoSession session, Throwable cause) throws Exception { cause.printStackTrace(); } public void messageReceived(IoSession session, Object message) throws Exception { String strMsg = message.toString(); if(strMsg.trim().equalsIgnoreCase("quit")) { session.close(); return; } Date date = new Date(); session.write(date.toString()); System.out.println("Message written"); } public void sessionIdle(IoSession session, IdleStatus status) throws Exception { System.out.println("IDLE"+session.getIdleCount(status)); } } 复制代码 测试: Client Output Server Output user@myhost:~> telnet 127.0.0.1 9123 Trying 127.0.0.1... Connected to 127.0.0.1. Escape character is '^]'. hello Wed Oct 17 23:23:36 EDT 2007 quit Connection closed by foreign host. user@myhost:~> MINA Time server started. Message written... 参考资料:1,MINA v2.0 Quick Start Guide 本文转自Phinecos(洞庭散人)博客园博客,原文链接:http://www.cnblogs.com/phinecos/archive/2008/11/27/1342655.html,如需转载请自行联系原作者

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Spring

Spring

Spring框架(Spring Framework)是由Rod Johnson于2002年提出的开源Java企业级应用框架,旨在通过使用JavaBean替代传统EJB实现方式降低企业级编程开发的复杂性。该框架基于简单性、可测试性和松耦合性设计理念,提供核心容器、应用上下文、数据访问集成等模块,支持整合Hibernate、Struts等第三方框架,其适用范围不仅限于服务器端开发,绝大多数Java应用均可从中受益。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册