GVKun编程网logo

vue2.0学习笔记(vue2.0教程)

16

在这篇文章中,我们将带领您了解vue2.0学习笔记的全貌,包括vue2.0教程的相关情况。同时,我们还将为您介绍有关asp.netcore2.0学习笔记、Vue2.0学习笔记之Vue中的compute

在这篇文章中,我们将带领您了解vue2.0学习笔记的全貌,包括vue2.0教程的相关情况。同时,我们还将为您介绍有关asp.net core2.0学习笔记、Vue 2.0学习笔记之Vue中的computed属性、Vue 2.0学习笔记之使用$refs访问Vue中的DOM、vue(1)--指令,回流与重绘---2019.5.20学习笔记的知识,以帮助您更好地理解这个主题。

本文目录一览:

vue2.0学习笔记(vue2.0教程)

vue2.0学习笔记(vue2.0教程)

Vue
 
特点
  • 易用(html,es5,css)
  • 灵活(核心库很小,压缩只有20k左右,渐进式技术栈[从易到难])
  • 性能(虚拟dom)
 
注意:vue不支持ie8以及以下的版本
 
对比
1 react和vue
  • 相似:只关注视图层,只帮助我们渲染(数据变化,重新渲染dom),使用 Virtual DOM,组件化,
  • vue的不同:不用像react把所有的虚拟dom都写在render函数里,它采用传统的dom模板,所以比react多一个指令
  • vue的优势:
1.在 React 应用中,当某个组件的状态发生变化时,它会以该组件为根,重新渲染整个组件子树。在 Vue 应用中,组件的依赖是在渲染过程中自动追踪的,所以系统能精确知晓哪个组件确实需要被重渲染。你可以理解为每一个组件都已经自动获得了 shouldComponentUpdate,并且没有上述的子树问题限制。
2.在react中所有的都是js。Vue 的整体思想是拥抱经典的 Web 技术,并在其上进行扩展。在 React 中,所有的组件的渲染功能都依靠 JSX。
  • vue的劣势:react拥有更丰富的生态圈
 
2 vue和angular
 
基础
  • new Vue({})里面传一个配置对象,再是一个data属性,data里面是数据。
 

 
2
  • el:挂载点,告诉当前应用的模板的位置(当前应用这个实例的模板的位置,让dom模板和实例相关联),将这个根实例挂载到app这个dom节点上,将dom当做根实例模板。(我们在new一个vue实例的时候,会去读这个el里面的所有的dom节点,把这些dom节点都当成模板)。
 

 
3 指令:当数据发生变化的时候,操作dom
  • 指令的作用:(数据的变化映射到dom的行为,就是做dom操作的地方,凡是带V-的是vue提供的特殊属性)
  • v-bind:  如果属性值是一个变量,在属性名前面加上v-bind
  • 条件: v-if:是否显示当前的dom节点(0,false不显示,其他的显示)
  • 遍历: v-for=''t in todos''  t遍历的每一个对象,in关键字,todos遍历的数组
  • 处理用户操作: v-on:绑定事件 v-on:click=''字符串'' 字符串是一个函数的引用,在一个methods配置项中进行配置,methods是用来配置模板中处理函数的地方
  • 双向绑定: v-model=''value''
  • 定义组件: 定义:Vue.component(''todo-item'', {
        template: ''<li>这是个待办项</li>''
        })
        使用:<todo-item></todo-item>
 

 
4 生命周期
      初始化之前
  • beforeCreate:  vue实例化之前        
  • created: vue实例化之后
  • beforeMount:插入真实的dom之前
  • mounted: 渲染之后
      初始化之后
  • beforeUpdate:当数据发生改变之前,虚拟dom重新渲染之前
  • updated:当数据发生改变之后
  • beforeDestroy:销毁实例之前
  • destroyed:销毁实例之后

 


 
5 语法
  • v-html:输出真正的 HTML。站点上动态渲染的任意 HTML 可能会非常危险,因为它很容易导致 XSS 攻击。请只对可信内容使用 HTML 插值,绝不要对用户提供的内容插值。
  • v-once:通过使用 v-once 指令,能执行一次性地插值,当数据改变时,插值处的内容不会更新。但请留心这会影响到该节点上所有的数据绑定
  • 双花括号中仅仅用来显示数据,加减乘除,三目运算符
  • 过滤器:当我们想要格式化显示文本的时候,比如后台返回来的数据是0和1,我们需要将他转成男和女。
 

 
6 class style
class:
  •  第一种,class后直接跟一个变量,这个变量必须为一个对象
     <div :hot''>
      hot:{
          red:ture
      }
  • 对象语法 <div :> active是class名,bool为变量值bool意思是如果bool值为true的话,就把key当做类名作用到当前的节点上,可以接受多个
  • 数组语法:把类放到数组中,数组中存在的就把类作用到当前的dom上,可以使用三目   
style:
     <div v-bind:></div>
     <div :>好困好困好困,我想睡觉睡觉</div>
 

 
7 条件渲染
  •  v-if和v-else 中间不能有第三者。
  • template:当我们想要显示和隐藏多个dom节点的时候使用,把一个 <template> 元素当做包装元素,并在上面使用 v-if。最终的渲染结果不会包含 <template> 元素
  • v-else-if 可以支持多选一
     <div v-if="type === ''A''">
          A
     </div>
     <div v-else-if="type === ''B''">
          B
     </div>
     <div v-else-if="type === ''C''">
          C
     </div>
     <div v-else>
          Not A/B/C
     </div  >
  • key:
       Vue 会尽可能高效地渲染元素,通常会复用已有元素而不是从头开始渲染。
       如果在两个template里面,v-if,v-else之间里面的dom节点相同的话,vue.js只会渲染一次第一个template里面的dom节点,复用,切换的时候也只会改变里面的值,上一次输入的数据会污染下一次的输入。当然不一定是template里面。
      <template v-if="loginType">
         <label>Username</label>
         <input placeholder="Enter your username">
      </template>
      <template v-else>
         <label>Email</label>
         <input placeholder="Enter your email address">
      </template>
      <button v-on:click=''change''>点击</button>
     只需添加一个具有唯一值的 key 属性即可
     <template v-if="loginType === ''username''">
        <label>Username</label>
        <input placeholder="Enter your username" key="username-input">
     </template>
     <template v-else>
        <label>Email</label>
        <input placeholder="Enter your email address" key="email-input">
     </template>
     注意, <label> 元素仍然会被高效地复用,因为它们没有添加 key 属性。
  • v-show
     v-show改变的只是dom节点的display而已,那个元素始终被渲染并保存在dom中,不支持template和v-else。而v-if会在切换的过程中重建和销毁dom节点。
     v-if是惰性的,如果在初始化这个dom节点之后,如果渲染的条件为假,就什么也不做,直到条件变为真的时候,才开始渲染。而v-show得话,不管初始的条件是什么,元素总是会被渲染。
     因此,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销,因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件不太可能改变,则使用 v-if 较好。
 

 
8 列表渲染
  • 根据一组数组的选项列表进行渲染,items是源数据数组,item是数组中的每个对象他的别名。index是索引,从0开始。
      v-for="(item , index) in items" ,可以与tamplate一起使用。
 
  • 可以遍历对象,value是每个对象的属性值,key为每个对象的属性名也就是key值。index为每个对象的索引。
     <template v-for=''(value,key,index) of oo''>
 
  • 整数迭代
     <p v-for=''n in 10''>{{n}}</p>
 
  • v-for 和 v-if
     v-for的优先级比v-if高,也就是说先把所有数据先遍历出来,遍历完了之后,再调用v-if,判断它是否被显示。
 
  • key
     <div v-for="item in items" :key="item.id"> key不能重复,这个key的作用就是当你改变数据的时候,vue能够快速的定位到你操作的哪位元素。
 
  • 数组更新检测的变化
   
    变异方法:push() pop() shift() unshift() splice() sort() reverse()
    所有的push,pop等方法都被vue进行重写了,这是因为vue无法检测到数组里面某一个对象的值发生了变化,目前js只能检测到对象里面的属性变化,没有办法检测这个对象发生了变化。当用vue重写后,调用push方法,会多做一些操作,触发视图的更新。那么vue是如何来进行数组方法的重写,Array.prototype.push=function(){},在这个函数里面来重写数据,触发视图的更新。
   
    重塑数组:filter(), concat(), slice()
    这些不会改变原始数组,但总是返回一个新数组。当使用非变异方法时,可以用新数组替换旧数组。
 
  • 显示过滤和排序
如果你想要进行数据筛选之后再进行显示的话,使用计算属性。
computed:{
  AAA(){
  return this.items.filter(function(item,index){
  console.log(index);
  return item.age>=18
  })
 }
}
 

 
9 事件处理器
  • 监听事件:
可以用 v-on 指令监听 DOM 事件来触发一些 JavaScript 代码。
<button v-on:click="counter += 1">增加 1</button>
 
  • 方法事件处理器
event对象:默认放在回调函数中的第一个参数
   1:当v-on跟的函数表达式没有参数的时候,就把event对象放入到回调函数的第一参数
   2:当回调函数跟了参数的时候,就要手动的传$event,<button v-on:click="al(2,$event)">注册</button>
在react中是不能在函数表达式中传递任何的参数,除非是箭头函数。
v-on:click="al"如果al函数没有参数,不需要写括号,如果al函数有参数,需要写括号。
 
  • 事件修饰符
      stop
      prevent
capture:把事件执行从冒泡阶段切换到捕获阶段
self:当current.targrt===target,就是你点击的元素就是绑定的那个事件的元素。触发事件的那个dom节点就是你绑定事件的dom节点。只当事件在该元素本身(比如不是子元素)触发时触发回调。
once:只执行一次,当执行下一次的时候,就remove掉。
     注意:使用修饰符时,顺序很重要;相应的代码会以同样的顺序产生。因此,用 @click.prevent.self 会阻止所有的点击,而 @click.self.prevent 只会阻止元素上的点击。
 
  • 键值修饰符
      .tab .enter .delete .esc .space .up .down .left .right
@keyup 原生的事件名
<input type="text" @keyup.enter=''show''>
哪一个键按下的时候,才执行对应的函数呢。
e.keyCode按键值
 
  • 修饰键
     .ctrl .alt .shift .meta
 
  • 滑鼠按键修饰符
     .left .right .middle
 
  • 为什么要在html中监听事件
     扫一眼 HTML 模板便能轻松定位在 JavaScript 代码里对应的方法。
     因为你无须在 JavaScript 里手动绑定事件,仅仅只需要在你的ViewModel里面写函数,不需要调用绑定事件这些方法,让你的ViewModel显得很纯粹。
     当一个 ViewModel 被销毁时,所有的事件处理器都会自动被删除。你无须担心如何自己清理它们。
 

 
10 表单控件绑定
v-model
  • 文本
     <input v-model="message" placeholder="edit me">
     <p>Message is: {{ message }}</p>
     当input的value发生变化的时候,v-model指令就会把当前input的value,通过v-model后面的变量把他重新赋值给data。
 
  • 多行文本
 
  • 复选框
   当单个复选框的时候,用v-model绑定一个变量的时候,返回的是这个checked的属性值,只有true或者false。<input type="checkbox" v-model="checked" checked/>
   多个复选框的时候,v-model必须等于同一个值,表示他们是一起的多选,这个值必须为一个数组,如果勾选上某一个checkbox,就会将这个checkbox的value放到数组里去。
   数据的(回显??):让复选框默认被勾上,直接在数组里面加上对应的value。
 
  • 单选框
   v-model等于一个变量,这个变量的值非数组
 
  • 单选列表
  <select v-model="selected">
  <option value="">请选择</option>
  <option value="CD">成都</option>
  <option value="LS">乐山</option>
  <option value="YA">雅安</option>
  </select> 
  selected是绑定在select上的,如果想让下面的option选中谁,就让selected等于option里面的innerHtml,但是一般不用innerhtml,所以就添加value。
  <select v-model="selected">
  <option value="">请选择</option>
  <option value="CD">成都</option>
  <option value="LS">乐山</option>
  <option value="YA">雅安</option>
  </select>
  <span>Selected: {{ selected }}</span>
  注意:如果option下面已经有value的话,就不会匹配innerhtml了。
  select 的 v-model要绑定 select这个dom上,当option有value的时候,这个变量就匹配的是value,否者就是option的innerHTML
 
  • 修饰符
     .lazy <input v-model.lazy="msg" >
     在默认情况下,v-model 在 input 事件中同步输入框的值与数据 (除了 上述IME 部分),但你可以添加一个修饰符 lazy ,从而转变为在 change 事件中同步,比如在做表单验证的时候,当这个表单失去焦点的时候,才提示。
     .number
     如果想自动将用户的输入值转为 Number 类型(如果原值的转换结果为 NaN 则返回原值),可以添加一个修饰符 number 给 v-model     来处理输入值:
     .trim
     如果要自动过滤用户输入的首尾空格,可以添加 trim 修饰符到 v-model 上过滤输入
 

 
11 组件
 
  • 什么是组件
 
     自定义的html元素,扩展html(因为对于前端来说,一切都是呈现)。“封装”了可重用的代码 。
 
  • 全局注册
 
     Vue.component(''my'',{
     template:`<div>今天天气很美</div>`
     })
     my:为组件名,组件是特殊的vue构造函数,在根实例配置对象中绝大部分配置项都可以在组件中一样使用,例如计算属性,data等,仅仅只是有一些配置项需要做特殊处理。
     template:模板
 
  • 局部注册
 
     var myCom={
       template:`<div>热热热</div>`
     }
     components:{
       ''my-com'':myCom
     }
     什么时候使用局部组件,什么时候使用全局组件:在你这个组件要在多个页面引入的时候,就是用全局组件,否则就是子组件。实际工作中一般使用局部组件,然后在实例或者组件中通过components来进行引用。
 
  • dom模板问题
 
     table里面会将组件踢出去,它只认得tr,td,thead这些标签
     浏览器在加载的时候,会先解析你的dom节点,然后就把你的组件踢出去了,这时候你的vue.js还没有执行。
     <tr is="my-cn"></tr>
     弄个假的tr,通过is这个属性告诉table这个tr是my-cn组件,浏览器解析这个dom节点的时候就不会把组件踢出去,当vue进行解析的时候,再将tr替换成vue组件
 
  • data 必须为函数
 
     如果data不是个函数,就会报错,因为组件在dom节点中是会被复用的,如果data为一个对象的话,多个组件引用的就是同一个对象里面的数据,同一个地址,点击其中一个组件的时候,会影响到其他对象。所以写一个data函数,为每个组件返回全新的 data 对象,vue会帮我们进行对象的拷贝,而不是复用地址。
 
  • 组件传递数据
 
     react中传递数据方式有两种,第一种是props,第二种是children,就是将组件里的innerhtml传递过去,接收的是所有的innerhtml,不太智能。
     在 Vue 中,父子组件的关系可以总结为 props down, events up。父组件通过 props 向下传递数据给子组件,子组件通过 events 给父组件发送消息
 
     1 使用props传递数据
     在子组件中props配置项中对传递的这个属性进行声明,
     var myCom={
  template:`<div>热热热{{count}}</div>`,
  props:[''count'']  //在子组件中我期望以count这个属性名来获取一些数据
     }
注意:如果你的属性是两个单词进行拼接的,那么在你进行props声明的时候,驼峰,但是在使用的时候,是小写加-。
Vue.component(''child'', {
props: [''myMessage''],
template: ''<span>{{ myMessage }}</span>''
})
<child my-message="hello!"></child>
 
  • 单向数据流
 
     为什么:如果修改了子组件里面的数据,进而修改了父组件的数据,这个数据又被这个父组件下的很多子组件使用,其他子组件的数据也会被修改,这时候就让这个数据流显得很混乱。
     但是为什么我们想要修改prop中的数据呢,因为我们想要把这个数据当做是自己的来使用。
 
  • prop验证
 
保证组件的健壮性。
props:{
  countApp:Number
}
var myCom1 = {
      template:`<div>勇敢一些!让你走的更远!{{myCount}}</div>`,
      /*props:[''myCount'']*/
      props:{ //验证属性的基本类型
          /*myCount:[Number,String]*/
          myCount:{
              type:Number,
              validator(value){
                  if(value>10){
                      return true
                  }
              }
              //default:2
              //required:true //这个属性必传
          }
      }
 };
validator:对传进来的数据进行具体的验证
 
  • 非props属性(有问题)
 
     可以将数据直接传入子组件,不需要在子组件的props中声明
 
  • 自定义事件
 
     在父组件中绑定一个自定义的事件,自定义事件的值就是父组件中的一个函数,传递这个自定义事件到子组件中,然后这个自定义事件就作为一个桥梁把父组件和子组件中的函数联系起来,在子组件中通过emit来触发在父组件中绑定的这个事件,也就是触发这个函数。
 
  • sync修饰符
 
      在子组件中修改父组件传递过来的一个数据,同时同步到父组件。
 
  • 使用自定义事件的表单输入组件
 
     自定义事件可以用来创建自定义的表单输入组件,使用 v-model 来进行数据双向绑定。
     <my-com color=''pink'' v-model=''msg''></my-com> 这是下面示例的语法糖
     <my-com color=''pink'' v-bind:value=''msg'' @input=''change''></my-com>
     所以说要让父组件中的v-model生效的花,在子组件中需要接受一个value属性,在输入的时候触发input事件
     var myCom={
  template:`<div>
  <input type=''text'' :[color]'' :value=''value'' @input=''change''/>
  </div>`,
  props:{
    color:{
      type:String,
      default:''red''
    },
    value:{
      type:String
    }
  },
  methods:{
     change(event){
        this.$emit(''input'',event.target.value)
     }
   }
 
  }
 
  • slot:分发
 
     在父组件中有多个innerHTML,通过<slot>在子组件里面得到它,如果想要区分每个slot的话,就在父组件中定义每个innerhtml的slot的值,然后在子组件中通过name属性,得到有与这个name属性相同的innerhtml。
     父:
     <span slot=''2''>哈哈哈</span>
     <span slot=''1''>呵呵呵</span>
     子:
     <slot name=''2''></slot>
     <input type=''text'' :[color]'' :value=''val'' @input=''change''/>
     <slot name=''1''></slot>
 
  • 动态组件
 
     如果你这个项目页面没有太多,不需要引入路由,就使用动态组件
     <div id="app">
       <div>
          <component :is="curPage"></component>
       </div>
     </div>
     定义一个保留的component元素在根实例模板app里面,通过is这个特性,跟着一个变量curPage,让这个变量不停的切换组件,让组件加载到component这里来。就是说让多个组件挂载到一个挂载点(一个根实例模板下面),进行动态的切换。
 
  • keep-alive
 
     在vue中,当组件被切换出去的时候,默认会将他des消除,切换回来的时候,再new。不能让组价进行复用。如果想要切换出去的组件仍然保留在内存中,就加一个keep-alive指令参数。
     <keep-alive>
        <component :is="curPage"></component>
     </keep-alive>
 
  • 子组件索引
 
     ref:在子组件中通过ref为子组件指定一个索引,在父组件中通过parent.$refs.profile得到这个子组件中相应的dom节点??
 
  • 异步组件
 
     当没有使用这个组件的时候,不会将这个组件下载下来,只有切换到这个组件之后,才将这个组件下载下来
 
  • 组件的关系:props down,events up
 
      props down:数据一般是由父组件通过属性向子组件传递,子组件在props中声明或者验证传过来的属性。
      events up:父组件通过传递一个自定义的事件,这个自定义事件的值就是对应的函数,它作为桥梁将子父组件联系起来,然后在子组件中通过$emit触发这个事件,也就是这父组件中的那个函数。
 

 
12 过度效果
 
  •  概念:元素从无到有,从有到无得一个过渡效果,所谓的过渡效果无非就是添加和删除类
 
  •  触发的条件:条件渲染(v-if),条件展示(v-show),动态组件,根渲染, Vue 提供了 transition 的封装组件,给条件变化组件添加enter, leave过渡,分为enter,enter-active,enter-to
 
  • 如何做动画效果
         用transition组件将p节点包裹起来,定义一个name值,这个name值就是vue来动态添加删除类的前缀。
   <transition name="fade">
   <p v-if="show">hello</p>
   </transition>
从无到有:
     . fade-enter:定义进入过渡的开始状态,. fade-enter {  opacity: 0  },下一步将要开始做动画的时候,干掉fade-enter。
     . fade-enter-active : 定义过渡的状态,. fade-enter-active {  transition: opacity .5s  }, 在元素整个过渡过程中作用。在元素被插入的时候生效。在 transition/animation 完成之后移除。 这个类可以被用来定义过渡的过程时间,延迟和曲线函数。
     . fade-enter-to :定义进入过渡的结束状态,不需要定义,因为元素默认的状态就是opacity : 1。
从有到无:
     . fade-leave:定义离开过渡的开始状态,opacity : 1,不需要定义,因为元素默认的状态就是opacity : 1。
     . fade-leave-active :定义过渡的状态,. fade-enter-active {  transition: opacity .5s  }, 在元素整个过渡过程中作用,在离开过渡被触发后立即生效,在 transition/animation 完成之后移除。 这个类可以被用来定义过渡的过程时间,延迟和曲线函数
     . fade-leave-to:定义离开过渡的结束状态,在离开过渡被触发一帧后生效(于此同时fade-leave 被删除),就是fade-leave被干掉之后马上触发,在 transition/animation 完成之后移除。
 
  • 什么时候使用css过渡(transition),什么时候使用css动画(animation)
           只有两帧的情况下使用过渡,当有多帧并且想要设置中间状态的时候设置中间状态使用动画。
  • css动画  
  • 自定义过渡类名
  • 自定义钩子 
         在做动画的时候干的一些事情
  • 初始化渲染
         可以通过 appear 特性设置节点的在初始渲染的过渡
         isux.tencent.com/css3/ 动画手册
 

 
13 自定义指令
     想做dom操作的时候,就封装指令。目前而言,基本用不到。
  • 钩子函数
     <input type="text" v-focus /> 
        Vue.directive(''focus'',{
  inserted(el){ //当第一次绑定的时候,还不是真实的dom节点
  el.focus()
  }
     })
     bind: 只调用一次,指令第一次绑定到元素时调用,用这个钩子函数可以定义一个在绑定时执行一次的初始化动作。这个时候这个dom还是虚拟dom,还不是真正的dom节点。
     inserted: 被绑定元素插入父节点时调用(父节点存在即可调用,不必存在于 document 中)。
     update: 被绑定元素所在的模板更新时调用,而不论绑定值是否变化。通过比较更新前后的绑定值,可以忽略不必要的模板更新(详细的钩子函数参数见下)。
     componentUpdated: 被绑定元素所在模板完成一次更新周期时调用。
     unbind: 只调用一次, 指令与元素解绑时调用。
 

 
14 路由
     干嘛的:切换组件
     用的版本:vue2的路由
 
代码:
  <script src="vue.js"></script>
  <script src="https://unpkg.com/vue-router/dist/vue-router.js"></script>
  </head>
  <body>
 
  <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>
  <router-link to="/user/wt">Go to User</router-link>
 
  </p>
  <!-- 路由出口 -->
 
  <!-- 路由匹配到的组件将渲染在这里 -->
 
  <!--当路由匹配到某个组件,加载到这里来。-->
  <router-view></router-view>
 
  </div>
 
  </body>
 
  <script>
 
 
  // 如果使用模块化机制编程,導入Vue和VueRouter,要调用 Vue.use(VueRouter)
 
  // 定义(路由)组件。
 
 
  // 可以从其他文件 import 进来
  const Foo = { template: `<div>foo
  <router-view></router-view>
  <router-link to="/foo/foo1">Go to foo1</router-link>
  <router-link to="/foo/foo2">Go to foo2</router-link>
  </div>`
  }
  const Foo1 = { template: ''<div>foo1</div>'' }
  const Foo2 = { template: ''<div>foo2</div>'' }
 
 
 
  //除了使用 <router-link> 创建 a 标签来定义导航链接,我们还可以借助 router 的实例方法,通过编写代码来实现。
  //通过js来进行组件的切换,称为编程式导航
  //router:路由实例,下面创建的,通过this.$router拿到路由实例
  //push会生成历史记录,replace不会生成历史记录,
  //go
  const Bar = { template: `<div>bar
           <button @click="goUser">Gouser</button>
  </div>`,
  data(){
  return {
  username:''haha''
  }
  },
  methods:{
  goUser(){
  //js跳转
// this.$router.push(''/user/13'') //路由实例
// this.$router.replace({path:"user/yangj2"})
  this.$router.push( {name:"user", params: { username:this.username},query: { plan: ''private'' } })
  }
  }
  }
 
  //响应路由参数的变化:
  //提醒一下,当使用路由参数时,例如从 /user/foo 导航到 user/bar,原来的组件实例会被复用。
  //因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。
  //不过,这也意味着组件的生命周期钩子不会再被调用。
  //怎么办呢,使用$watch,来监测$route对象
  //$route为路由对象,当路由匹配到谁的时候,就会把当前路由匹配的所有的信息注入到这个路由对象中,并把这个对象赋到当前这个组件当中
  //to:切换过去新的路由对象 from:切换之前的路由对象
 
  //beforeRouteEnter,组件内部的钩子,比create这些钩子都要先调用,是路由钩子
  //在beforeRouteEnter之前,是拿不到组件的实例对象的,不能再这个钩子里面去调用this,因为这个组件都还没有被实例化
  //不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
 
  //beforeRouteUpdate;在当前路由改变,但是组件被复用的时候调用
  const User = { template: ''<div>user---{{$route.params.username }}---{{msg}}</div>'',
  data(){
  return {
  $route:''$route'',
  msg:''我的爱在等待''
  }
  },
// created(){
// console.log(''呵呵'');
// },
  mounted(){
           console.log(this.$route);
  },
  beforeRouteEnter(to, from, next){
// console.log(22);
// next();
  setTimeout(function(){
  next((vm)=>{
  vm.msg="我的已回来!"
  })
  console.log(this.msg);
  },2000)
  },
  beforeRouteUpdate(to, from, next){//当组件被复用的时候
  console.log("fuyong");
  next();
  },
  watch:{
  $route (to,from){
// console.log(from,to);
  }
  }
 
  }
 
  // 定义路由
  // 每个路由应该映射一个组件。 其中"component" 可以是
  // 通过 Vue.extend() 创建的组件构造器,
  // 或者,只是一个组件配置对象。
 
  // 命名路由
 
  //重定向:如果没有任何匹配的路由,重定向到某一个组件,跟在最后面
  const routes = [ //路由匹配规则
  { path: ''/foo'', component: Foo ,children:[
  {
  path:''foo1'',
  component:Foo1
  },
  {
  path:''foo2'',
  component:Foo2
  }
 
  ]},
  { path: ''/bar'', component: Bar },
  { path: ''/user/:username'',component:User,name: ''user''},
  { path:''/'', redirect:''/foo''}
  ]
 
 
  // 创建 router 实例,然后传 routes 配置
  // 你还可以传别的配置参数, 不过先这么简单着吧。
 
  //告诉路由器实例我们的路由匹配规则是什么
 
  //history:路由匹配模式 ,路径切换
  const router = new VueRouter({
  routes // (缩写)相当于 routes: routes
  })
 
 
  //因为vue没有类似于react的should的钩子,它并不能对接收的数据进行限制,
  //所以就增加了导航钩子,vue-router 提供的导航钩子主要用来拦截导航,让它完成跳转或取消。
 
  //router.beforeEach,每一次路由切换的时候,都会执行的全局钩子
  //next,一定要调用他,1)如果全部钩子都执行完了,则导航的状态就是确认的。next(false),当前切换的url会被重置到from路由对应的地址,next(''/'')跳转到对应的地址上去
  //afterEach:一般不用,用户都切换过来了,还弄啥呢
  router.beforeEach((to, from, next) => {
// console.log(to);
// if (true) { //登陆失效
// next();
// };
  next();
 
  })
 
 
  // 创建和挂载根实例。
  // 记得要通过 router 配置参数注入路由,
  // 从而让整个应用都有路由功能
 
  //将路由实例传给vue这个实例,告诉当前app你使用了路由
  const app = new Vue({
  el:''#app'',
  router
  })
  // 现在,应用已经启动了!
  </script>
</html>
 

 
15 安装vue-cli
1)npm i -g vue-cli
2)vue init webpack test
3)npm i
4)npm run dev

16 vue-cli项目
bulid:不用管,是用来帮我们启服务地方,唯一要操作的地方是dev-server,因为要在这里面进行数据的模拟。
config:一些配置文件
static:不能够通过npm安装的包,像zepto,swiper这些库,静态资源文件,想通过标签引入
src:所有项目存在的地方
assets:静态资源存放的地方,但是可以通过import导入。
main.js :项目的启动项,webpack的入口文件,el和template共存的话,就会把id为app的组件全部替换成template包含的组件。
App.vue :整个项目最外层的那个组件
stylus:css预编译语言,scoped:当前的css只作用到当前的模板上面
router-link-active:通过router-link切换的切换连接,切换到哪一个组件,会加上这个类
 
懒加载:创建异步组件,Vue.component,第二个参数为函数。当打包构建应用时,Javascript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。结合 Vue 的 异步组件 和 Webpack 的 code splitting feature, 轻松实现路由组件的懒加载。
我们要做的就是把路由对应的组件定义成异步组件:
const User = r => require.ensure([], () => r(require(''@/pages/User.vue'')), ''User'')
不让组件同时加载进来,只有当用户点击切换的时候,才到服务器里面去读,将这个组件下载下来。才用require.ensure分开打包。
enusre:代码的分割,把组件放到了require.ensure里面的时候,webpack就知道要分开打包,不会放到主bundl里面,当路由匹配到这个组件的时候,再来下载这个异步组件。
第一个参数[ ] ,当去加载后面的User.vue组件的时候,这个参数是前置依赖的意思,就是你这个组件依赖于哪些组价。
第二个参数r是resolve的意思,当我们加载require后面的组件的时候去调用resolve函数
第三个参数是分组的意思,取成一样的,告诉webpack将这两个组件打包在一起
 
静态资源文件,例如static文件里面的reset文件,在index.html文件中用link标签进行引入
 
asset里面通过相对路径来进行访问,static通过绝对路径进行访问,静态资源目录在根目录,因为能够直接访问index.html页面
 
当我们使用模块化导入vue-router或者其他插件的时候,必须调用Vue.use(Router),把这个导入的路由放在Vue.use里面去,script标签就不需要放到Vue.use里面去,因为script标签将这个router挂载到window对象上去,全局就可以访问。但是组件化这块,它是一个独立的作用域,在他的内部是不会把他的变量暴露到全局上去,所以动态调用Vue.use(Router)
 
npm run build 打包
 
vue.js的插件 很多
 

vuex
 
仓库:用来存储数据,改变数据的地方,在所有的页面中,都导入这个仓库,如果想要修改仓库的数据的话,就执行特定的函数,让仓库执行修改。
 
vuex:状态管理,就是一个仓库
 
状态管理:
state:数据
view:视图,从状态中拿数据
action:在视图中触发一个函数,函数中修改状态
 
安装vuex,cnpm i --save vuex    
有1,2的版本,安装的是2版本
 
state:在仓库里面具体存储数据的地方,谷堆,state,一个对象包含了所有的数据,一个仓库就只有一个state,一个谷堆
通过在实例中传递store 选项,提供了一种机制将状态从根组件『注入』到每一个子组件中。如果想要在组价中获取state之中的状态的话,通过this.$store.state.count获取
 
mapState:当一个组件需要获取多个状态时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键,如果计算属性与state属性一样的时候可以使用这个参数,可以在数组里传入字符串,表示把this.$store.state.count映射到this.count这个计算属性
数组语法:这个语法不好的是万一有这个组件自身的计算属性的话,就不好操作了
computed:mapState([
       ''count'',
       ''count2''
  ])  
对象展开语法:对象扩展运算符的,把对象的 key value 展开成用,隔开的一个一个的键值对,为了与局部就是组件内部的计算属性混合使用,需要使用一个工具将多个对象合并在一起,采用对象的展开运算符
  ...mapState([
      ''count'',
      ''count2''
  ])
mapState函数返回的是一个对象
 
getters:是state的扩展,有时候我们需要从store的state中派生一些状态,例如对列表进行过滤,Vuex 允许我们在store中定义 getters (可以认为是store的计算属性),getter接受state作为第一个参数。
getters:{
  old(state){
  return state.users && state.users.filter(function(item){
  return item.age>=20;
  })
  }
  }
filter函数:filter()方法中行参是一个回调函数.这个回调函数就是一个规则,返回一个布尔值.filter()方法会对数组中每一个元素使用这个回调函数.注意,这里说的是每一个元素.并且将返回值为true的元素装入一个新数组返回
mapGetters:mapGetters 辅助函数仅仅是将 store 中的 getters 映射到局部计算属性中。
 
 
Mutations: 改变state的地方,更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutations 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数。不能直接调用hander, 这个选项就像注册事件,触发一个 increment的mutation,你需要以相应的type调用 store.commit(‘[type]’)。必须为同步函数,没有异步操作,因为vue.js写了一个工具,来一步一步监听我们的数据
mutations:{
  increment(state,num){
  state.count+=num;
  }
  }
methods:{
  add(){
  this.$store.commit(''increment'',2);
  }
  }
 
mapMutations:语法糖,这种方法无法传参数
mutations:{
     increment(state){
       state.count++;
     }
  }
methods:{
     ...mapMutations({
         add:''increment''
      })
  }
如果mutation为一个变量的话,那样如果mutation中的key,也就是自定义的type类型发生改变,那么很多地方都需要改。所以推荐使用常量代替mutation的type(类型)用不用常量取决于你 —— 在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做。
es6是允许对象的key是一个变量的,但是要用方括号把他给括起来
 
Action:专门用来做异步操作,当异步操作结束后,再提交mutation,类似于mutation,action 提交 mutation,不是直接修改state,action可以包含任意异步操作。
Action 函数接受一个与 store 实例具有相同方法和属性的 context 对象,因此你可以调用 context.commit 提交一个 mutation,或者通过 context.state 和 context.getters 来获取 state 和 getters。
数据放在dev.server.js里面,并且要放在app实例之后之前,因为webpack-dev-middleware这样的中间件,帮我们自动配置了history api cback,前段发过来的任何请求,都返回一个index.html。
increment({commit,state,getters}){
    $.get(''/api/list'', function(data) {
         console.log(data);
         commit(''MOREUSERS'',data.list)
      });
  }
Action 通过 store.dispatch 方法触发:
getuser(){
  this.$store.dispatch(''increment'')
  }
mapActions:
  ...mapActions({
     getuser:''increment''
  })

 
注意:如果组件名为驼峰命名的话,比如myCom,在html里面引入的话,要是用my-com。对于自定义标签名,Vue.js 不强制要求遵循 W3C 规则 (小写,并且包含一个短杠),尽管遵循这个规则比较好。局部注册的对象名不能加-。以后局部注册的时候就使用驼峰myCom,在components里面定义组件名的时候就使用my-com,在html中引入的时候就使用my-com。
 
注意:
 
    * {{}}里面只能用来显示数据的
    * vue.js将当前data里面的所有属性都挂载到了vue实例上去了
    * 组件是特殊的vue实例,只有组件才可以被销毁
    * 带$的符号一般是vue.js内部的方法,一般不使用
    * h5的新的api classList
 
vue最强大的功能是计算属性和组件
es6遍历器 fa? keys?
 
新姿势:
1 语法糖:它意指那些没有给计算机语言添加新功能,而只是对人类来说更“甜蜜”的语法。语法糖往往给程序员提供了更实用的编码方式,有益于更好的编码风格,更易读。不过其并没有给语言添加什么新东西。
 
 
 
重点                                                                                                                                                                                                                                              
什么时候用到计算属性:模板中出现太多的逻辑,使用了过多的表达式,这样会让代码变得难以维护的时候使用。同时想要监听某些数据,当这些数据发生变化的时候会影响到其他的数据的时候使用。
计算属性:
 
1.
  • 在配置对象中定义computed:{计算属性名:function(){ return 计算好的数据}}
2.特点:
  • 1.会监听在这个计算属性中所依赖的任何数据,
  • 2.计算属性会被缓存(当依赖的数据没有变化,多次使用的时候不会执行计算属性函数),
  • 3.当依赖的数据发生变化的时候,计算属性的值会被重新计算并赋值。
  • 4.get和set还可以监听计算属性自身的变化
4.getter和setter
  • getter一般是获取计算属性的值
  • setter当计算属性发生变化的时候,同时也会执行set,来修改原本依赖的数据
 
计算属性 VS 方法调用:计算属性会被缓存(当依赖的数据没有变化,多次使用的时候不会执行计算属性函数),函数的调用会在使用的地方多次执行函数,造成资源的浪费。
计算属性 VS Watch: Watch:只能一次监听一个数据变化(代码的冗余)。当你想在数据变化做异步操作时,使用它。watch的优点是可以设置中间状态。
 
 
 
<!DOCTYPE html>
<html>
       <head>
                 <meta charset="UTF-8">
                 <title></title>
                 <script src="https://unpkg.com/vue"></script>
       </head>
       <body>
                 <div id="app">
                          {{message}}
                          <span v-bind:title="message">指令是vue中有的而react没有</span>
                          <p v-if="bool">这里是vue的条件判断,与react不同的是react是在render的return之前使用if else进行判断或者直接在return里面使用三目运算符进行判断</p>
                          <ul>
                                    <li v-for="t in tobos">{{t.text}}</li>
                                    <todo-item></todo-item>
                          </ul>
                          <button v-on:click="change">这里演示vue的事件绑定</button>
                          <input type="text" v-model="val" />
                          <div v-html="span"></div>
                 </div>
       </body>
       <script>
                 //v-html能够将html字符串转化为html,但是对于这条指令要谨慎使用,我们一般使用{{}}引入变量,在{{}}中只能使用加减乘除已经三目运算符等简单的JavaScript语句与react类似,使用v-bind来使属性值改变
                 //指令的参数在指令后以冒号指明,比如v-bind:title="message"的参数是title,v-on:click="change"的产生是click
                 //指令的修饰符是以半角句号 . 指明的特殊后缀,比如v-on:click.prevent="change"的.prevent就是修饰符,表示触发本事件的时候会调用event.preventDefault()
                 Vue.component("todo-item",{
                          template:"<li>组件的使用就是像使用dom节点标签一样</li>"
                 })
                 var app=new Vue({
                          el:"#app",
                          data:{
                                    message:"今天开始学vue",
                                    bool:false,
                                    span:"<spancolor:red''>双花括号只能将数据转化为纯文本而不是html,因此我们一般使用v-html:''变量''来转化html字符串</span>",
                                    tobos:[{
                                             text:"这是为了试验vue中的遍历"
                                    },{
                                             text:"vue中的遍历与react中的遍历不同,react一般使用map()方法返回一个数组,而vue直接使用v-for指令进行遍历"
                                    },{
                                             text:"v-for=''遍历到的每一项  in 数组名''"
                                    }],
                                    val:"使用v-model实现的input中数据的双向绑定"
                          },
                          methods:{
                                    change(){
                                             alert("绑定的函数配置在methods对象下面");
                                             this.bool=true
                                    }
                          },
                          beforeCreate(){
                                    //在将data挂载到实例以及初始化事件之前调用
                                    console.log("在将data挂载到实例以及初始化事件之前调用");
                          },
                          created(){
                                    //在实例创建好之后调用
                                    console.log("在实例创建好之后调用")
                          },
                          beforeMount(){
                                    //判断实例中是否配置el和template后调用
                                    console.log("判断实例中是否配置el和template后调用") 
                          },
                          mounted(){
                                    //使用vue.js编译代码,成功插入dom后调用
                                    console.log("使用vue.js编译代码,成功插入dom后调用")
                          },
                          beforeUpdate(){
                                    //数据发生改变,虚拟dom还没有重新渲染之前调用
                                    console.log("数据发生改变,但是虚拟Dom还没重新之前调用")
                          },
                          updated(){
                                    //数据发生改变,虚拟dom重新渲染之后调用
                                    console.log("数据发生改变,虚拟dom重新渲染之后调用")
                                    console.log(this.message)
                          },
                          //在生命周期钩子函数中我们是能够拿到当前data中的数据的
                          beforeDestroy(){
                                    //在调用$destory()后调用
                                    console.log("在调用$destory()后调用")
                          },
                          destroyed(){
                                    console.log("从真实dom中移除")
                          },
                          //一般不销毁实例,组件是特殊的实例,被销毁的一般是组件
                 })
       </script>
</html>

asp.net core2.0学习笔记

asp.net core2.0学习笔记

一、Core

  1,防止过度发布

  2,Main

  3,Startup

  4,添加过滤器

  5,依赖注入

  6,中间件

  7,静态文件

  8,路由

  9,环境

  10,配置和选项

  11,日志

  12,使用Sesstion

  13,使用po文件配置本地化

  14,在 ASP.NET 管道中运行 OWIN 中间件

  15,WebSockets

  16,使用内存缓存

二、EF

  1,Include和ThenInclude

  2,通过依赖关系注入注册上下文

  3,种子数据

  4,级联删除

  5,组合PK

  6,使用原始sql

三、Razor页面

四、MVC

  1,模型绑定

  2,视图

  3,标记帮助程序

  4,内置标记帮助程序

  5,分部视图

  6,视图组件

  7,上传文件

  8,筛选器

  9,绑定与压缩

五、Model

六、配置

 

一、Core

1,防止过度发布

①TryUpdateModelAsync

public async Task<IActionResult> OnPostAsync()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }
            var emptyStudent = new Student();
            if (await TryUpdateModelAsync<Student>(emptyStudent, "student", s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
            {
                _context.Students.Add(emptyStudent);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");

            }
            return null;
        }
code
        [BindProperty]
        public Student Student { get; set; }
        public async Task<IActionResult> OnPostAsync(int? id)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

            var studentToUpdate =await _context.Students.FindAsync(id);
            if (await TryUpdateModelAsync<Student>(studentToUpdate, "student", s => s.LastName, s => s.EnrollmentDate))
            {
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            return Page();
        }
code2

仅更新TryUpdateModelAsync列出的值

在上述示例中:

  • 第二个自变量 ("student", // Prefix) 是用于查找值的前缀。 该自变量不区分大小写。
  • 已发布的表单值通过模型绑定转换为 Student 模型中的类型。

 ②通过属性名称匹配

using System;

namespace ContosoUniversity.Models
{
    public class StudentVM
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }
    }
}
StudentVM
[BindProperty]
public StudentVM StudentVM { get; set; }

public async Task<IActionResult> OnPostAsync()
{
    if (!ModelState.IsValid)
    {
        return Page();
    }

    var entry = _context.Add(new Student());
    entry.CurrentValues.SetValues(StudentVM);
    await _context.SaveChangesAsync();
    return RedirectToPage("./Index");
}
code

 

2,Main

using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

namespace aspnetcoreapp
{
    public class Program
    {
        public static void Main(string[] args)
        {
            BuildWebHost(args).Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}
Main

UseHttpSys:用于在 HTTP.sys 中托管应用

UseContentRoot:用于指定根内容目录

Build 和 Run 方法生成 IWebHost 对象,该对象托管应用并开始侦听 HTTP 请求

 UseStartup:指定Startup 类

 

3,Startup

ConfigureServices 定义应用所使用的服务(如 ASP.NET Core MVC、Entity Framework Core 和Identity)(可选择)

Configure 定义请求管道的中间件(必须)

ConfigureServices在 Configure之前执行

UseMvc 扩展方法将路由中间件添加到请求管道,并将 MVC 配置为默认设置。 

 

4,添加过滤器

①定义Middleware

using System;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using WebApplication1.Models;

namespace WebApplication1
{
    public class RequestSetOptionsMiddleware
    {
        private readonly RequestDelegate _next;
        private IOptions<AppOptions> _injectedOptions;

        public RequestSetOptionsMiddleware(
            RequestDelegate next, IOptions<AppOptions> injectedOptions)
        {
            _next = next;
            _injectedOptions = injectedOptions;
        }

        public async Task Invoke(HttpContext httpContext)
        {
            Console.WriteLine("RequestSetOptionsMiddleware.Invoke");

            var option = httpContext.Request.Query["option"];

            if (!string.IsNullOrWhiteSpace(option))
            {
                _injectedOptions.Value.Option = WebUtility.HtmlEncode(option);
            }

            await _next(httpContext);
        }
    }
}
RequestSetOptionsMiddleware

②实现IStartupFilter

using Microsoft.AspNetCore.Hosting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;

namespace WebApplication1
{
    public class RequestSetOptionsStartupFilter : IStartupFilter
    {
        public Action<IApplicationBuilder> Configure(Action<IApplicationBuilder> next)
        {
            return builder =>
            {
                builder.UseMiddleware<RequestSetOptionsMiddleware>();
                next(builder);
            };
        }
    }
}
RequestSetOptionsStartupFilter

③在 ConfigureServices 的服务容器中注册 IStartupFilter

// This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddTransient<IStartupFilter, RequestSetOptionsStartupFilter>();
            services.AddMvc();
        }
Code

 

5,依赖注入

①注册案例

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseInMemoryDatabase()
    );

    // Add framework services.
    services.AddMvc();

    // Register application services.
    services.AddScoped<ICharacterRepository, CharacterRepository>();
    services.AddTransient<IOperationTransient, Operation>();
    services.AddScoped<IOperationScoped, Operation>();
    services.AddSingleton<IOperationSingleton, Operation>();
    services.AddSingleton<IOperationSingletonInstance>(new Operation(Guid.Empty));
    services.AddTransient<OperationService, OperationService>();
}
View Code

 

6,中间件

①中间件排序

Configure方法中定义的中间的顺序决定请求的顺序,响应的顺序则相反

②Use、Run 和 Map

Run:不会调用next方法。后面的管道将不会执行

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            
            app.Run(async (context)=>{
                 await context.Response.WriteAsync("Map Test 1");
            });

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            //使用wwwroot
            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

           
        }
Run

Use:如果中间件不调用next方法,会使管道短路。

Map:如果请求路径以给定路径开头,则执行分支。

public class Startup
{
    private static void HandleMapTest1(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 1");
        });
    }

    private static void HandleMapTest2(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            await context.Response.WriteAsync("Map Test 2");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.Map("/map1", HandleMapTest1);

        app.Map("/map2", HandleMapTest2);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}
Map

public class Startup
{
    private static void HandleBranch(IApplicationBuilder app)
    {
        app.Run(async context =>
        {
            var branchVer = context.Request.Query["branch"];
            await context.Response.WriteAsync($"Branch used = {branchVer}");
        });
    }

    public void Configure(IApplicationBuilder app)
    {
        app.MapWhen(context => context.Request.Query.ContainsKey("branch"),
                               HandleBranch);

        app.Run(async context =>
        {
            await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
        });
    }
}
MapWhen 基于给定谓词的结果创建请求管道分支

app.Map("/level1", level1App => {
       level1App.Map("/level2a", level2AApp => {
           // "/level1/level2a"
           //...
       });
       level1App.Map("/level2b", level2BApp => {
           // "/level1/level2b"
           //...
       });
   });
Map 支持嵌套
app.Map("/level1/level2", HandleMultiSeg);
Map 还可同时匹配多个段

③自定义Middleware

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using System.Globalization;
using System.Threading.Tasks;

namespace IocDemo
{
    public class CustomMiddleware
    {
        private readonly RequestDelegate _next;

        public CustomMiddleware(RequestDelegate next)
        {
            _next=next;
        }
        public Task InvokeAsync(HttpContext context)
        {
            this._next.Invoke(context); 
            context.Response.WriteAsync("CustomMiddleware");
            return Task.CompletedTask;
        }
    }
    public static class CustomMiddlewareExtensions
    {
        public static IApplicationBuilder UseCustomer(this IApplicationBuilder budiler)
        {
            return budiler.UseMiddleware<CustomMiddleware>();
        }

    }

   
}
CustomMiddleware
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace IocDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            //使用自定义middleware
            app.UseCustomer();

            //使用wwwroot
            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

           
        }
    }
}
Startup

 

7,静态文件

提供 Web 根目录外的文件

请求 http://<server_address>/StaticFiles/images/banner1.svg 提供 banner1.svg 文件

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(); // For the wwwroot folder

    app.UseStaticFiles(new StaticFileOptions
    {
        FileProvider = new PhysicalFileProvider(
            Path.Combine(Directory.GetCurrentDirectory(), "MyStaticFiles")),
        RequestPath = "/StaticFiles"
    });
}
View Code

设置 HTTP 响应标头

public void Configure(IApplicationBuilder app)
{
    app.UseStaticFiles(new StaticFileOptions
    {
        OnPrepareResponse = ctx =>
        {
            // Requires the following import:
            // using Microsoft.AspNetCore.Http;
            ctx.Context.Response.Headers.Append("Cache-Control", "public,max-age=600");
        }
    });
}
View Code

③静态文件授权方法

新建个静态文件根文件夹

[Authorize]
public IActionResult BannerImage()
{
    var file = Path.Combine(Directory.GetCurrentDirectory(), 
                            "MyStaticFiles", "images", "banner1.svg");

    return PhysicalFile(file, "image/svg+xml");
}
View Code

④默认提供文档

public void Configure(IApplicationBuilder app)
{
    app.UseDefaultFiles();
    app.UseStaticFiles();
}
View Code

要提供默认文件,必须在 UseStaticFiles 前调用 UseDefaultFiles。 UseDefaultFiles 实际上用于重写 URL,不提供文件。 通过 UseStaticFiles 启用静态文件中间件来提供文件。

 

public void Configure(IApplicationBuilder app)
{
    // Serve my app-specific default file, if present.
    DefaultFilesOptions options = new DefaultFilesOptions();
    options.DefaultFileNames.Clear();
    options.DefaultFileNames.Add("mydefault.html");
    app.UseDefaultFiles(options);
    app.UseStaticFiles();
}
将默认文件名更改为 mydefault.html

 

8,路由

routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id?}");
典型路由
routes.MapRoute(
    name: "default",
    template: "{controller=Home}/{action=Index}/{id:int}");
路由约束

①尾随句点  .  是可选: files/{filename}.{ext?} 

②*全方位参数: blog/{*slug} 

 将匹配以 /blog 开头且其后带有任何值(将分配给 slug 路由值)的 URI

 

9,环境

ASP.NET Core 在应用程序启动时读取环境变量 ASPNETCORE_ENVIRONMENT(如果未设置 ASPNETCORE_ENVIRONMENT,将默认为 Production

ASPNETCORE_ENVIRONMENT值:

  • Development(开发)
  • Production(生产)
  • Staging(暂存)

 

10,配置和选项

nuget Microsoft.Extensions.Configuration 

①读取json文件

nuget Microsoft.Extensions.Configuration.Json 

using System;
using System.IO;
// Requires NuGet package
// Microsoft.Extensions.Configuration.Json
using Microsoft.Extensions.Configuration;

public class Program
{
    public static IConfiguration Configuration { get; set; }

    public static void Main(string[] args = null)
    {
        var builder = new ConfigurationBuilder()
            .SetBasePath(Directory.GetCurrentDirectory())
            .AddJsonFile("appsettings.json");

        Configuration = builder.Build();

        Console.WriteLine($"option1 = {Configuration["Option1"]}");
        Console.WriteLine($"option2 = {Configuration["option2"]}");
        Console.WriteLine(
            $"suboption1 = {Configuration["subsection:suboption1"]}");
        Console.WriteLine();

        Console.WriteLine("Wizards:");
        Console.Write($"{Configuration["wizards:0:Name"]}, ");
        Console.WriteLine($"age {Configuration["wizards:0:Age"]}");
        Console.Write($"{Configuration["wizards:1:Name"]}, ");
        Console.WriteLine($"age {Configuration["wizards:1:Age"]}");
        Console.WriteLine();

        Console.WriteLine("Press a key...");
        Console.ReadKey();
    }
}
View Code
{
  "option1": "value1_from_json",
  "option2": 2,

  "subsection": {
    "suboption1": "subvalue1_from_json"
  },
  "wizards": [
    {
      "Name": "Gandalf",
      "Age": "1000"
    },
    {
      "Name": "Harry",
      "Age": "17"
    }
  ]
}
json文件内容

 节点由冒号  :  分隔: Configuration["subsection:suboption1"] 

 获取数组值: Configuration["wizards:0:Name"] 

②读取xml文件

nuget Microsoft.Extensions.Configuration.Xml 

using System;
using Microsoft.Extensions.Configuration;
using System.IO;

namespace ConsoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var builder= new ConfigurationBuilder()
                        .SetBasePath(Directory.GetCurrentDirectory())
                        .AddXmlFile("test.xml");
            var configuration=builder.Build();
            Console.WriteLine(configuration["wizard:Harry:age"]);
        }
    }
}
View Code
<wizards>
  <wizard name="Gandalf">
    <age>1000</age>
  </wizard>
  <wizard name="Harry">
    <age>17</age>
  </wizard>
</wizards>
xml文件内容

③json绑定到对象里面

nuget Microsoft.Extensions.Configuration.Binder 

using System;
using Microsoft.Extensions.Configuration;
using System.IO;
using System.Collections.Generic;

namespace ConsoleDemo
{
    class Program
    {
        static void Main(string[] args)
        {
            var dic=new Dictionary<string,string>();
            dic.Add("Person:Name","hunter");
            dic.Add("Person:Age","10");


            var builder=new ConfigurationBuilder()
                        .AddInMemoryCollection(dic);
            IConfiguration configuration=builder.Build();

            var person=new Person();
            configuration.GetSection("Person").Bind(person);
            Console.WriteLine(person.Name);
            Console.WriteLine(person.Age);

        }
    }

    public class Person{
        public string Name{get;set;}
        public int Age{get;set;}
    }
}
View Code

 ④在razor视图获取mvc视图中使用configuration

@page
@model IndexModel

@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index Page</title>
</head>
<body>
    <h1>Access configuration in a Razor Pages page</h1>
    <p>Configuration[&quot;key&quot;]: @Configuration["key"]</p>
</body>
</html>
在 Razor 页面页中
@using Microsoft.Extensions.Configuration
@inject IConfiguration Configuration

<!DOCTYPE html>
<html lang="en">
<head>
    <title>Index View</title>
</head>
<body>
    <h1>Access configuration in an MVC view</h1>
    <p>Configuration[&quot;key&quot;]: @Configuration["key"]</p>
</body>
</html>
在 MVC 视图中

 ④注入option配置

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Option":{
    "option1":"value1_from_json",
    "option2":-1
  }
 
}
appsettings.json
  public class MyOption{
        public string option1{get;set;}
        public int option2{get;set;}
    }
自定义MyOption类
services.Configure<MyOption>(Configuration.GetSection("Option"));
在ConfigureServices注册
private  IOptions<MyOption> _option;
        public BlogController(IOptions<MyOption> option)
        {
            _option=option;
           _option.Value.option1+_option.Value.option2
        }
构造注入并使用

 

11,日志

①创建日志

nuget Microsoft.Extensions.Logging 

public class TodoController : Controller
{
    private readonly ITodoRepository _todoRepository;
    private readonly ILogger _logger;

    public TodoController(ITodoRepository todoRepository,
        ILogger<TodoController> logger)
    {
        _todoRepository = todoRepository;
        _logger = logger;
    }
要创建日志,请先从依赖关系注入容器获取 ILogger 对象
public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}
然后在该记录器对象上调用日志记录方法

②事件ID

LoggingEvents自己定义例如:

public class LoggingEvents
{
    public const int GenerateItems = 1000;
    public const int ListItems = 1001;
    public const int GetItem = 1002;
    public const int InsertItem = 1003;
    public const int UpdateItem = 1004;
    public const int DeleteItem = 1005;

    public const int GetItemNotFound = 4000;
    public const int UpdateItemNotFound = 4001;
}
LoggingEvents
public IActionResult GetById(string id)
{
    _logger.LogInformation(LoggingEvents.GetItem, "Getting item {ID}", id);
    var item = _todoRepository.Find(id);
    if (item == null)
    {
        _logger.LogWarning(LoggingEvents.GetItemNotFound, "GetById({ID}) NOT FOUND", id);
        return NotFound();
    }
    return new ObjectResult(item);
}
使用案例

 

12,使用Sesstion

 using Microsoft.AspNetCore.Http 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;

namespace MvcDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            HostingEnvironment=env;
        }

        public IConfiguration Configuration { get; }
        public IHostingEnvironment HostingEnvironment{get;}

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            var physicalProvider= HostingEnvironment.ContentRootFileProvider;
            services.AddSingleton<IFileProvider>(physicalProvider);

            services.AddSession(option=>{
                option.IdleTimeout=TimeSpan.FromSeconds(10);
                option.Cookie.HttpOnly=true;//指示客户端脚本是否可以访问cookie。
            });

            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseSession();//要在UseMvc之前调用

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
Startup
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo.Models;
using Microsoft.Extensions.FileProviders;
using Microsoft.AspNetCore.Http;

namespace MvcDemo.Controllers
{
    public class SesstionController : Controller
    {

        public string Index(string str)
        {
            HttpContext.Session.SetString("str",str);
            
            return "sesstion已经设置";
        }

        public string GetSession()
        {
           var val= HttpContext.Session.GetString("str");
           return val;
        }
        

    }
}
使用

 

13,使用po文件配置本地化

nuget OrchardCore.Localization.Core 

①配置Startup

using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Localization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.FileProviders;

namespace MvcDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration, IHostingEnvironment env)
        {
            Configuration = configuration;
            HostingEnvironment=env;
        }

        public IConfiguration Configuration { get; }
        public IHostingEnvironment HostingEnvironment{get;}

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            var physicalProvider= HostingEnvironment.ContentRootFileProvider;
            services.AddSingleton<IFileProvider>(physicalProvider);

            services.AddSession(option=>{
                option.IdleTimeout=TimeSpan.FromSeconds(10);
                option.Cookie.HttpOnly=true;//指示客户端脚本是否可以访问cookie。
            });

            services.AddLocalization(option=>{

            });
            services.AddMvc()
                    .AddViewLocalization();
            services.AddPortableObjectLocalization(options=>{
                options.ResourcesPath="Localization";
            });
            services.Configure<RequestLocalizationOptions>(options =>
            {
                var supportedCultures = new List<CultureInfo>
                {
                    new CultureInfo("en-US"),
                    new CultureInfo("en"),
                    new CultureInfo("fr-FR"),
                    new CultureInfo("fr"),
                    new CultureInfo("zh-CHS")
                };

                options.DefaultRequestCulture = new RequestCulture("zh-CHS");
                options.SupportedCultures = supportedCultures;
                options.SupportedUICultures = supportedCultures;
            });

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseSession();//要在UseMvc之前调用

            app.UseStaticFiles();

            app.UseRequestLocalization();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
配置Startup

②使应用内容可本地化

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo.Models;
using Microsoft.Extensions.Localization;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Builder;
using System.Globalization;
using Microsoft.AspNetCore.Localization;

namespace MvcDemo.Controllers
{
    public class LocalizationController : Controller
    {

        private readonly IStringLocalizer<LocalizationController> _localization;

        public LocalizationController(IStringLocalizer<LocalizationController> localization)
        {
            _localization=localization;
        }

        public string Index()
        {
            return _localization["Localization"];
        }

        public string Get()
        {
            return _localization["Test{0}",1];
        }

    }
}
LocalizationController

③视图本地化

@using Microsoft.AspNetCore.Mvc.Localization
@inject IViewLocalizer Localizer
@{
    ViewData["Title"] = "Home Page";
}
<p>@Localizer["Localization"]</p>
<p>@Localizer["Test{0}",1]</p>
视图

④DataAnnotations本地化

⑤po文件

msgid "Localization"
msgstr "Localization"

msgid "Home"
msgstr "Home"

msgid "Test{0}"
msgstr "Test{0}"
en.po
msgid "Localization"
msgstr "本地化"

msgid "Home"
msgstr "主页"

msgid "Test{0}"
msgstr "测试{0}"
zh-CHS.po

msgid:键

msgstr:值

 

14,在 ASP.NET 管道中运行 OWIN 中间件

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.Owin;
using System.Text;
using System.IO;
using System.Globalization;

namespace AspNetCoreOwin
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseOwin(pipeline=>{
                pipeline(next=>OwinHello);
            });

            app.UseStaticFiles();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        public Task OwinHello(IDictionary<string,object> environment)
        {
            string responseText="hello owin";
            byte[] responseBytes=Encoding.UTF8.GetBytes(responseText);
            var responseStream=(Stream)environment["owin.ResponseBody"];
            var responseHeaders=(IDictionary<string,string[]>)environment["owin.ResponseHeaders"];
            responseHeaders["Content-Length"]=new string[]{responseBytes.Length.ToString(CultureInfo.InvariantCulture)};
            responseHeaders["Content-Type"]=new string[] {"text/plain"};
            return responseStream.WriteAsync(responseBytes,0,responseBytes.Length);
        }
    }
}
Startup

案例下载:https://pan.baidu.com/s/1FLfBuqsMuKnv7wSF3BSW4w 

 

15,WebSockets

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.AspNetCore.WebSockets;
using System.Net.WebSockets;
using Microsoft.AspNetCore.Http;
using System.Threading;
using System.Collections.Concurrent;

namespace WebSocketsDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddMvc();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseDefaultFiles();
            app.UseStaticFiles();

            var webSocketOption=new WebSocketOptions()
            {
                KeepAliveInterval=TimeSpan.FromSeconds(120),//向客户端发送“ping”帧的频率,以确保代理保持连接处于打开状态
                ReceiveBufferSize=4*1024//用于接收数据的缓冲区的大小
            };
            app.UseWebSockets(webSocketOption);

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });

            app.Use(async (context,next)=>{
                if(context.Request.Path=="/ws")
                {
                    if(context.WebSockets.IsWebSocketRequest)//判断是否是websocket请求
                    {
                        //将TCP连接升级到WebSocket连接,并提供websocket对象
                        WebSocket webSocket=await context.WebSockets.AcceptWebSocketAsync();
                        WebSockets.TryAdd(webSocket,"1");
                        await Echo(context,webSocket);
                    }
                    else
                    {
                        context.Response.StatusCode=400;
                    }
                }
                else
                {
                    await next();
                }
            });
        }

        private async Task Echo(HttpContext context,WebSocket websocket)
        {
            var buffer=new byte[4*1024];
            WebSocketReceiveResult result=await websocket.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);
            while(!result.CloseStatus.HasValue)
            {
                //广播
                foreach(var ws in WebSockets)
                {
                    if(!ws.Key.CloseStatus.HasValue)
                    {
                       await ws.Key.SendAsync(new ArraySegment<byte>(buffer,0,buffer.Length),result.MessageType,result.EndOfMessage,CancellationToken.None);
                    }
                    else
                    {
                        string value;
                        WebSockets.TryRemove(ws.Key,out value);
                    }
                    //await ws.SendAsync(new ArraySegment<byte>(buffer,0,buffer.Length),result.MessageType,result.EndOfMessage,CancellationToken.None);
                }
                
                result=await websocket.ReceiveAsync(new ArraySegment<byte>(buffer),CancellationToken.None);
            }
            await websocket.CloseAsync(result.CloseStatus.Value,result.CloseStatusDescription,CancellationToken.None);
        }
        //可以开一个线程去检测WebSocket是否掉线,掉线则从字典中删除
        private static ConcurrentDictionary<WebSocket,string> WebSockets=new ConcurrentDictionary<WebSocket,string>();
    }
}
Startup
<!doctype html>
<html lang="en">
  <head>
    <title>Title</title>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">

    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <script src="lib/jquery/dist/jquery.min.js"></script>
    </head>
  <body>
    
    <input type="text" id="message"/><input type="button" id="send" value="发送"/>

    <div id="context"></div>

    <!-- Optional JavaScript -->
    <!-- jQuery first, then Popper.js, then Bootstrap JS -->
    <script src="https://code.jquery.com/jquery-3.2.1.slim.min.js" integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN" crossorigin="anonymous"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js" integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q" crossorigin="anonymous"></script>
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js" integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl" crossorigin="anonymous"></script>
  </body>

  <script>

      var ws;
      if("WebSocket" in window)
      {
         ws=new WebSocket("ws://localhost:5000/ws");
         ws.onopen=function(){
          alert("开始")
         }
         ws.onmessage=function(res){
           $("#context").append(res.data+"<br/>")
         }
         ws.onclose=function(){
           alert("退出")
         }
      }
      else
      {
          alert("不支持websocket");
      }

   $(function(){

          $("#send").click(function(){
            ws.send($("#message").val())
          })
   })

  </script>
</html>
index.html

案例下载:https://pan.baidu.com/s/1C5CLrtD6Mr66oiM7sfEHaw

 

16,使用内存缓存

nuget Microsoft.Extensions.Caching.Memory 

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Caching;

namespace CacheDemo
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddMemoryCache();
            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                app.UseHsts();
            }

            app.UseHttpsRedirection();
            app.UseStaticFiles();
            app.UseCookiePolicy();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
Startup
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using CacheDemo.Models;
using Microsoft.Extensions.Caching.Memory;

namespace CacheDemo.Controllers
{
    public class HomeController : Controller
    {

        private IMemoryCache _cache;

        public HomeController(IMemoryCache cache)
        {
            _cache=cache;
        }

        public IActionResult Index()
        {
            DateTime date;
            if(_cache.TryGetValue("date",out date))
            {
                ViewData["date"]=date;
            }
            else
            {
                ViewData["date"]="未设置缓存";
            }
            return View();
        }


        public IActionResult Set()
        {
            _cache.Set<DateTime>("date",DateTime.Now);
            return RedirectToAction("Index");
        }

        
    }
}
HomeController

案例下载:https://pan.baidu.com/s/1DEpF-_HLlEQFWswPyb-WkA

 

 

二、EF

1,Include和ThenInclude

使上下文加载 Student.Enrollments 导航属性,并在每个注册中加载 Enrollment.Course 导航属性

public class Student
    {
        public int ID { get; set; }
        public string LastName { get; set; }
        public string FirstMidName { get; set; }
        public DateTime EnrollmentDate { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }

    public class Enrollment
    {
        public int EnrollmentID { get; set; }
        public int CourseID { get; set; }
        public int StudentID { get; set; }
        public Grade? Grade { get; set; }

        public Course Course { get; set; }
        public Student Student { get; set; }
    }
    public class Course
    {
        //主键
        [DatabaseGenerated(DatabaseGeneratedOption.None)]
        public int CourseID { get; set; }
        public string Title { get; set; }
        public int Credits { get; set; }

        public ICollection<Enrollment> Enrollments { get; set; }
    }
实体

2,通过依赖关系注入注册上下文

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<SchoolContext>(options =>
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

    services.AddMvc();
}

配置连接字符串

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=ContosoUniversity1;ConnectRetryCount=0;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}
appsettings.json

ConnectRetryCount=0 来防止 SQLClient 挂起 

3,种子数据

using ContosoUniversity.Models;
using System;
using System.Linq;

namespace ContosoUniversity.Data
{
    public static class DbInitializer
    {
        public static void Initialize(SchoolContext context)
        {
            context.Database.EnsureCreated();

            // Look for any students.
            if (context.Students.Any())
            {
                return;   // DB has been seeded
            }

            var students = new Student[]
            {
            new Student{FirstMidName="Carson",LastName="Alexander",EnrollmentDate=DateTime.Parse("2005-09-01")},
            new Student{FirstMidName="Meredith",LastName="Alonso",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Arturo",LastName="Anand",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Gytis",LastName="Barzdukas",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Yan",LastName="Li",EnrollmentDate=DateTime.Parse("2002-09-01")},
            new Student{FirstMidName="Peggy",LastName="Justice",EnrollmentDate=DateTime.Parse("2001-09-01")},
            new Student{FirstMidName="Laura",LastName="Norman",EnrollmentDate=DateTime.Parse("2003-09-01")},
            new Student{FirstMidName="Nino",LastName="Olivetto",EnrollmentDate=DateTime.Parse("2005-09-01")}
            };
            foreach (Student s in students)
            {
                context.Students.Add(s);
            }
            context.SaveChanges();

            var courses = new Course[]
            {
            new Course{CourseID=1050,Title="Chemistry",Credits=3},
            new Course{CourseID=4022,Title="Microeconomics",Credits=3},
            new Course{CourseID=4041,Title="Macroeconomics",Credits=3},
            new Course{CourseID=1045,Title="Calculus",Credits=4},
            new Course{CourseID=3141,Title="Trigonometry",Credits=4},
            new Course{CourseID=2021,Title="Composition",Credits=3},
            new Course{CourseID=2042,Title="Literature",Credits=4}
            };
            foreach (Course c in courses)
            {
                context.Courses.Add(c);
            }
            context.SaveChanges();

            var enrollments = new Enrollment[]
            {
            new Enrollment{StudentID=1,CourseID=1050,Grade=Grade.A},
            new Enrollment{StudentID=1,CourseID=4022,Grade=Grade.C},
            new Enrollment{StudentID=1,CourseID=4041,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=1045,Grade=Grade.B},
            new Enrollment{StudentID=2,CourseID=3141,Grade=Grade.F},
            new Enrollment{StudentID=2,CourseID=2021,Grade=Grade.F},
            new Enrollment{StudentID=3,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=1050},
            new Enrollment{StudentID=4,CourseID=4022,Grade=Grade.F},
            new Enrollment{StudentID=5,CourseID=4041,Grade=Grade.C},
            new Enrollment{StudentID=6,CourseID=1045},
            new Enrollment{StudentID=7,CourseID=3141,Grade=Grade.A},
            };
            foreach (Enrollment e in enrollments)
            {
                context.Enrollments.Add(e);
            }
            context.SaveChanges();
        }
    }
}
DbInitializer
// Unused usings removed
using System;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using ContosoUniversity.Data;

namespace ContosoUniversity
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var host = BuildWebHost(args);

            using (var scope = host.Services.CreateScope())
            {
                var services = scope.ServiceProvider;
                try
                {
                    var context = services.GetRequiredService<SchoolContext>();
                    DbInitializer.Initialize(context);
                }
                catch (Exception ex)
                {
                    var logger = services.GetRequiredService<ILogger<Program>>();
                    logger.LogError(ex, "An error occurred while seeding the database.");
                }
            }

            host.Run();
        }

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .Build();
    }
}
Program.cs

第一次运行该应用时,会使用测试数据创建并填充数据库。 更新数据模型时:

  • 删除数据库。
  • 更新 seed 方法。
  • 运行该应用,并创建新的种子数据库。


4,级联删除 

modelBuilder.Entity<Department>()
   .HasOne(d => d.Administrator)
   .WithMany()
   .OnDelete(DeleteBehavior.Restrict)
Code

 

5,组合PK

public class CourseAssignment
    {
        public int InstructorID { get; set; }
        public int CourseID { get; set; }
        public Instructor Instructor { get; set; }
        public Course Course { get; set; }
    }
Model
protected override void OnModelCreating(ModelBuilder modelBuilder)
        {
modelBuilder.Entity<CourseAssignment>()
                .HasKey(c => new { c.CourseID, c.InstructorID });
 }
SchoolContext

 

 6,使用原始sql

调用返回实体的查询

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    string query = "SELECT * FROM Department WHERE DepartmentID = {0}";
    var department = await _context.Departments
        .FromSql(query, id)
        .Include(d => d.Administrator)
        .AsNoTracking()
        .SingleOrDefaultAsync();

    if (department == null)
    {
        return NotFound();
    }

    return View(department);
}
FromSql

调用返回其他类型的查询

public async Task<ActionResult> About()
{
    List<EnrollmentDateGroup> groups = new List<EnrollmentDateGroup>();
    var conn = _context.Database.GetDbConnection();
    try
    {
        await conn.OpenAsync();
        using (var command = conn.CreateCommand())
        {
            string query = "SELECT EnrollmentDate, COUNT(*) AS StudentCount "
                + "FROM Person "
                + "WHERE Discriminator = ''Student'' "
                + "GROUP BY EnrollmentDate";
            command.CommandText = query;
            DbDataReader reader = await command.ExecuteReaderAsync();

            if (reader.HasRows)
            {
                while (await reader.ReadAsync())
                {
                    var row = new EnrollmentDateGroup { EnrollmentDate = reader.GetDateTime(0), StudentCount = reader.GetInt32(1) };
                    groups.Add(row);
                }
            }
            reader.Dispose();
        }
    }
    finally
    {
        conn.Close();
    }
    return View(groups);
}
Code

调用更新查询

[HttpPost]
public async Task<IActionResult> UpdateCourseCredits(int? multiplier)
{
    if (multiplier != null)
    {
        ViewData["RowsAffected"] = 
            await _context.Database.ExecuteSqlCommandAsync(
                "UPDATE Course SET Credits = Credits * {0}",
                parameters: multiplier);
    }
    return View();
}
Code

 

 

 

 

三、Razor页面

1,路由

<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>
<a href="/Students/Edit?studentID=6">Edit</a>

2,命令搭建基架

参考文档:https://docs.microsoft.com/zh-cn/aspnet/core/data/ef-rp/intro#add-scaffold-tooling 

3,SelectList

using ContosoUniversity.Data;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.AspNetCore.Mvc.Rendering;
using Microsoft.EntityFrameworkCore;
using System.Linq;

namespace ContosoUniversity.Pages.Courses
{
    public class DepartmentNamePageModel : PageModel
    {
        public SelectList DepartmentNameSL { get; set; }

        public void PopulateDepartmentsDropDownList(SchoolContext _context,
            object selectedDepartment = null)
        {
            var departmentsQuery = from d in _context.Departments
                                   orderby d.Name // Sort by name.
                                   select d;

            DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(),
                        "DepartmentID", "Name", selectedDepartment);
        }
    }
}


  <div class="form-group">
                <label asp-for="Course.Department" class="control-label"></label>
                <select asp-for="Course.DepartmentID" class="form-control"
                        asp-items="@Model.DepartmentNameSL">
                    <option value="">-- Select Department --</option>
                </select>
                <span asp-validation-for="Course.DepartmentID" class="text-danger" />
            </div>
Code

 

 

 

四、MVC

1,模型绑定 

 ①常用的验证属性

  • [CreditCard]:验证属性是否具有信用卡格式。

  • [Compare]:验证某个模型中的两个属性是否匹配。

  • [EmailAddress]:验证属性是否具有电子邮件格式。

  • [Phone]:验证属性是否具有电话格式。

  • [Range]:验证属性值是否落在给定范围内。

  • [RegularExpression]:验证数据是否与指定的正则表达式匹配。

  • [Required]:将属性设置为必需属性。

  • [StringLength]:验证字符串属性是否最多具有给定的最大长度。

  • [Url]:验证属性是否具有 URL 格式。

 

2,视图

①Razor语法

<p>Last week this time: @(DateTime.Now - TimeSpan.FromDays(7))</p>
计算 @() 括号中的所有内容,并将其呈现到输出中
@{
    var joe = new Person("Joe", 33);
}

<p>Age@(joe.Age)</p>
使用显式表达式将文本与表达式结果串联起来
@Html.Raw("<span>Hello World</span>")
输出不进行编码,但呈现为 HTML 标记
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    <text>Name: @person.Name</text>
}
带分隔符的显式转换
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    @:Name: @person.Name
}
使用 @ 的显式行转换
@if (value % 2 == 0)
{
    <p>The value was even.</p>
}
else if (value >= 1337)
{
    <p>The value is large.</p>
}
else
{
    <p>The value is odd and small.</p>
}
@if、else if、else
@switch (value)
{
    case 1:
        <p>The value is 1!</p>
        break;
    case 1337:
        <p>Your number is 1337!</p>
        break;
    default:
        <p>Your number wasn''t 1 or 1337.</p>
        break;
}
@switch
@for (var i = 0; i < people.Length; i++)
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>
}
@for
@foreach (var person in people)
{
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>
}
@foreach
@{ var i = 0; }
@while (i < people.Length)
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>

    i++;
}
@while
@{ var i = 0; }
@do
{
    var person = people[i];
    <p>Name: @person.Name</p>
    <p>Age: @person.Age</p>

    i++;
} while (i < people.Length);
@do while
@using (Html.BeginForm())
{
    <div>
        email:
        <input type="email" id="Email" value="">
        <button>Register</button>
    </div>
}
复合语句 @using
@try
{
    throw new InvalidOperationException("You did something invalid.");
}
catch (Exception ex)
{
    <p>The exception message: @ex.Message</p>
}
finally
{
    <p>The finally statement.</p>
}
@try、catch、finally

②@inherits 指令对视图继承的类提供完全控制

using Microsoft.AspNetCore.Mvc.Razor;

public abstract class CustomRazorPage<TModel> : RazorPage<TModel>
{
    public string CustomText { get; } = "Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below.";
}
自定义 Razor 页面类型
@inherits CustomRazorPage<TModel>

<div>Custom text: @CustomText</div>
CustomText 显示在视图中
<div>Custom text: Gardyloo! - A Scottish warning yelled from a window before dumping a slop bucket on the street below.</div>
呈现

@functions 指令允许 Razor 页面将 C# 代码块添加到视图中

@functions {
    public string GetHello()
    {
        return "Hello";
    }
}

<div>From method: @GetHello()</div>
视图
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Razor;

public class _Views_Home_Test_cshtml : RazorPage<dynamic>
{
    // Functions placed between here 
    public string GetHello()
    {
        return "Hello";
    }
    // And here.
#pragma warning disable 1998
    public override async Task ExecuteAsync()
    {
        WriteLiteral("\r\n<div>From method: ");
        Write(GetHello());
        WriteLiteral("</div>\r\n");
    }
#pragma warning restore 1998
生成的 Razor C# 类

④_ViewImports.cshtml导入共享指令

 支持的指令:

  • @addTagHelper

  • @removeTagHelper

  • @tagHelperPrefix

  • @using

  • @model

  • @inherits

  • @inject

 针对 ASP.NET Core MVC 应用的 _ViewImports.cshtml 文件通常放置在 Views 文件夹中

 

3,标记帮助程序

①@addTagHelper

@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper MvcDemo2.TagHelpers.EmailTagHelper,MvcDemo2
@addTagHelper之后第一个参数:需要加载的标记帮助类(*表示所有)
第二个参数:标记帮助类所在的程序集

②简单的将 <email>hunter</email> 变成 <a>hunter</a>  

using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
    //EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
    public class EmailTagHelper:TagHelper
    {
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName="a";//用<a>标签替换<email>
        }

    }
}
①添加EmailTagHelper

@using MvcDemo2
@using MvcDemo2.Models
@addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
@addTagHelper MvcDemo2.TagHelpers.EmailTagHelper,MvcDemo2
②修改_ViewImports.cshtml

这时,在页面上的 <email>hunter</email> 会变成<a>hunter</a> 

③设置自结束标签 <email/>  

[HtmlTargetElement("email",TagStructure=TagStructure.WithoutEndTag)]
    public class EmailTagHelper:TagHelper
HtmlTargetElement

如果存在不是自结束标签,就会报错

④将 <email mail-to="hunter"></email> 变成 <a href="hunter@qq.com">hunter@qq.com</a> 

using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
    //EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
    public class EmailTagHelper:TagHelper
    {
        public const string EmailDomain="qq.com";
        //可以通过<email mail-to =“...”/>传递
        //标记帮助程序采用 Pascal 大小写格式的类和属性名将转换为各自相应的小写短横线格式
        public string MailTo{get;set;}
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName="a";//用<a>标签替换<email>
            var address=MailTo+"@"+EmailDomain;

            //给标签添加属性
            output.Attributes.SetAttribute("href",address);
            //设置<email></email>里面的内容
            output.Content.SetContent(address);
        }

    }
}
EmailTagHelper

⑤将 <email>hunter</email> 变成<a href="hunter@qq.com">hunter@qq.com</a> 

using Microsoft.AspNetCore.Razor.TagHelpers;
using System.Threading.Tasks;
namespace MvcDemo2.TagHelpers
{
    //EmailTagHelper 的根名称是 email,因此 <email> 标记将作为目标名称
    public class EmailTagHelper:TagHelper
    {
        public const string EmailDomain="qq.com";
        //可以通过<email mail-to =“...”/>传递

        public override async Task ProcessAsync(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName="a";//用<a>标签替换<email>

            var content=await output.GetChildContentAsync();//获取标签里的内容
            var tagter=content.GetContent()+"@"+EmailDomain;

            //给标签添加属性
            output.Attributes.SetAttribute("href",tagter);
            //设置<email></email>里面的内容
            output.Content.SetContent(tagter);
        }

    }
}
EmailTagHelper

⑥将页面中的 <bold>bbbb</bold> 替换为 <strong>bbbb</strong>  

using Microsoft.AspNetCore.Razor.TagHelpers;
namespace MvcDemo2.TagHelpers
{
    public class BoldTagHelper:TagHelper
    {
        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.Attributes.RemoveAll("bold");
            output.PreContent.SetHtmlContent("<strong>");
            output.PostContent.SetHtmlContent("</strong>");
        }
        
    }
}
BoldTagHelper

③将model传入标记帮助程序

using System;
namespace MvcDemo2.Models
{
    public class WebsiteContext
    {
        public Version Version{get;set;}
        public int CopyrightYear{get;set;}
        public bool Approved{get;set;}
        public int TagsToShow{get;set;}
    }
}
Model
using Microsoft.AspNetCore.Razor.TagHelpers;
using MvcDemo2.Models;
namespace MvcDemo2.TagHelpers
{

    //使用<website-information />
    public class WebsiteInformationTagHelper:TagHelper
    {
        public WebsiteContext Info{get;set;}

        public override void Process(TagHelperContext context, TagHelperOutput output)
        {
            output.TagName="section";
            output.Content.SetHtmlContent(
                $@"<ul><li><strong>版本:</strong> {Info.Version}</li>
                <li><strong>版权 年:</strong> {Info.CopyrightYear}</li>
                <li><strong>是否批准:</strong> {Info.Approved}</li>
                <li><strong>显示的标签:</strong> {Info.TagsToShow}</li></ul>"
            );
        }
        
    }
}
WebsiteInformationTagHelper
<website-information info="new WebsiteContext(){
    Version=new Version(1,3),
    CopyrightYear=10,
    Approved=true,
    TagsToShow=131
}"></website-information>
Html

⑥.NET 类型和生成的 HTML 类型 

.NET 类型 输入类型 Bool type=”checkbox” String type=”text” DateTime type=”datetime” Byte type=”number” Int type=”number” Single、Double type=”number”

⑦数据注解和生成的 HTML 类型 

 

特性 输入类型 [EmailAddress] type=”email” [Url] type=”url” [HiddenInput] type=”hidden” [Phone] type=”tel” [DataType(DataType.Password)] type=”password” [DataType(DataType.Date)] type=”date” [DataType(DataType.Time)] type=”time”

⑧验证标记帮助程序

 <span asp-validation-for="Email"></span> 

针对具有 asp-validation-summary 属性的 <div> 元素

asp-validation-summary 显示的验证消息
ValidationSummary.All 属性和模型级别
ValidationSummary.ModelOnly 模型
ValidationSummary.None

实例: 

@model RegisterViewModel
@{
    ViewData["Title"] = "Home Page";
}

<a bold>aaa</a>
<bold>bbbb</bold>
<!--Razor 知道 info 属性是一个类,而不是字符串,并且你想要编写 C# 代码。 编写任何非字符串标记帮助程序属性时,都不应使用 @@ 字符。-->


<website-information info="new WebsiteContext(){
    Version=new Version(1,3),
    CopyrightYear=10,
    Approved=true,
    TagsToShow=131
}"></website-information>



<form asp-action="Register" method="POST" role="form">
    <legend>注册</legend>

    <div asp-validation-summary="ModelOnly"></div>

    <div class="form-group">
        <input asp-for="Email" class="form-control" placeholder="Input field">
        <span asp-validation-for="Email"></span>
    </div>

     <div class="form-group">
        <input asp-for="Password" class="form-control" placeholder="Input field">
        <span asp-validation-for="Password"></span>
    </div>

    <button type="submit" class="btn btn-primary">Submit</button>
</form>
Index.cshtml
        [HttpPost]
        public IActionResult Register(RegisterViewModel model)
        {
            return View("Index");
        }
Controller

⑨选择标记帮助程序

using System.Collections.Generic;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MvcDemo2.Models
{
    public class CountryViewModel
    {
        public string Country{get;set;}
        public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
            new SelectListItem(){Value="MX",Text="墨西哥"},
            new SelectListItem(){Value="CA",Text="加拿大"},
            new SelectListItem(){Value="US",Text="美国"}
        };
    }
}
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models;

namespace MvcDemo2.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var country=new CountryViewModel();
            country.Country="US";
            return View(country);
        }

        [HttpPost]
        public IActionResult Index(CountryViewModel model)
        {
            return View(model);
        }
        
    }
}
HomeController
@model CountryViewModel


<form method="POST" asp-action="Index">

    <select asp-for="Country" asp-items="Model.Countries"></select>

    <input type="submit" value="提交" />
</form>
Index

⑩枚举绑定

public enum CountryEnum
        {
            [Display(Name = "墨西哥合众国")]
            Mexico,
            [Display(Name = "美国")]
            USA,
            Canada,
            France,
            Germany,
            Spain
        }
定义枚举

 <select asp-for="Country" asp-items="Html.GetEnumSelectList<CountryEnum>()"></select> 

⑩选项分组

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MvcDemo2.Models
{
    public class CountryViewModel
    {
        public string Country{get;set;}

        public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
            new SelectListItem(){Value="MX",Text="墨西哥",Group=new SelectListGroup(){Name="分组一"}},
            new SelectListItem(){Value="CA",Text="加拿大",Group=new SelectListGroup(){Name="分组二"}},
            new SelectListItem(){Value="US",Text="美国",Group=new SelectListGroup(){Name="分组三"}}
        };

        
    }
      
}
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models;

namespace MvcDemo2.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var country=new CountryViewModel();
            country.Country="US";
            return View(country);
        }

        [HttpPost]
        public IActionResult Index(CountryViewModel model)
        {
            return View(model);
        }
        
    }
}
HomeController

 <select asp-for="Country" asp-items="Model.Countries"></select> 

 

 ⑩多重选择

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MvcDemo2.Models
{
    public class CountryViewModel
    {
        public IEnumerable<string> Countrys{get;set;}

        public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
            new SelectListItem(){Value="MX",Text="墨西哥"},
            new SelectListItem(){Value="CA",Text="加拿大"},
            new SelectListItem(){Value="US",Text="美国"}
        };

        
    }
      
}
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models;

namespace MvcDemo2.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var country=new CountryViewModel();
            country.Countrys=new []{"US","MX"};
            return View(country);
        }

        [HttpPost]
        public IActionResult Index(CountryViewModel model)
        {
            return View(model);
        }
        
    }
}
HomeController
@model CountryViewModel


<form method="POST" asp-action="Index">

    <select asp-for="Countrys" asp-items="Model.Countries"></select>

    <input type="submit" value="提交" />
</form>
index

⑩无选定内容

using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Mvc.Rendering;

namespace MvcDemo2.Models
{
    public class CountryViewModel
    {
        public string Country{get;set;}

        public List<SelectListItem> Countries {get;}=new List<SelectListItem>(){
            new SelectListItem(){Value="MX",Text="墨西哥"},
            new SelectListItem(){Value="CA",Text="加拿大"},
            new SelectListItem(){Value="US",Text="美国"}
        };

        
    }
      
}
CountryViewModel
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using MvcDemo2.Models;

namespace MvcDemo2.Controllers
{
    public class HomeController : Controller
    {
        public IActionResult Index()
        {
            var country=new CountryViewModel();
            return View(country);
        }

        [HttpPost]
        public IActionResult Index(CountryViewModel model)
        {
            return View(model);
        }
        
    }
}
HomeController
@model CountryViewModel


<form method="POST" asp-action="Index">

    <select asp-for="Country" asp-items="Model.Countries">
        <option value="">--none--</option>
    </select>

    <input type="submit" value="提交" />
</form>
index

 

4,内置标记帮助程序

asp-all-route-data

@{
var parms = new Dictionary<string, string>
            {
                { "speakerId", "11" },
                { "currentYear", "true" }
            };
}

<a asp-route="speakerevalscurrent"
   asp-all-route-data="parms">Speaker Evaluations</a>
试图
<a href="/Speaker/EvaluationsCurrent?speakerId=11&currentYear=true">Speaker Evaluations</a>
前面的代码生成以下 HTML:
[Route("/Speaker/EvaluationsCurrent",
       Name = "speakerevalscurrent")]
public IActionResult Evaluations(
    int speakerId,
    bool currentYear) => View();
Controller

asp-fragment

可定义要追加到 URL 的 URL 片段

<a asp-controller="Speaker"
   asp-action="Evaluations"
   asp-fragment="SpeakerEvaluations">Speaker Evaluations</a>
试图
<a href="/Speaker/Evaluations#SpeakerEvaluations">Speaker Evaluations</a>
生成的 HTML:

asp-area

设置相应路由的区域名称

<a asp-area="Blogs"
   asp-controller="Home"
   asp-action="AboutBlog">About Blog</a>
试图
<a href="/Blogs/Home/AboutBlog">About Blog</a>
生成的 HTML:

asp-protocol

在URL 中指定协议(比如 https

<a asp-protocol="https"
   asp-controller="Home"
   asp-action="About">About</a>
试图
<a href="https://localhost/Home/About">About</a>
生成的 HTML:

asp-host

在 URL 中指定主机名

<a asp-protocol="https"
   asp-host="microsoft.com"
   asp-controller="Home"
   asp-action="About">About</a>
试图
<a href="https://microsoft.com/Home/About">About</a>
生成的 HTML:

 ⑥缓存标记帮助程序

 <cache enabled="true">@DateTime.Now</cache> 

 属性expires-after:设置过期时间

<cache expires-after="@TimeSpan.FromSeconds(120)">
    Current Time Inside Cache Tag Helper: @DateTime.Now
</cache>
View Code

 属性expires-sliding:设置多久未被访问过期设置

<cache expires-sliding="@TimeSpan.FromSeconds(60)">
    Current Time Inside Cache Tag Helper: @DateTime.Now
</cache>
View Code

 ⑦环境标记帮助程序

<environment include="Development">Development</environment>
<environment include="Staging">Staging</environment>
<environment include="Production">Production</environment>
不同的环境显示不同的标签

⑧图像标记帮助程序

<img src="~/images/1.jpg"  asp-append-version="true"/>
View Code

asp-append-version:追加版本号

 

5,分部视图

@await Html.PartialAsync("ViewName")
@await Html.PartialAsync("ViewName.cshtml")
@await Html.PartialAsync("~/Views/Folder/ViewName.cshtml")
@await Html.PartialAsync("/Views/Folder/ViewName.cshtml")
@await Html.PartialAsync("../Account/LoginPartial.cshtml")
@model string
姓名:@Model
分布页面Person.cshtml
@await Html.PartialAsync("Person","Hunter")
调用

 

6,视图组件

①添加 ViewComponent 类

在根目录新建一个ViewComponents文件夹,建ViewComponent类放在此文件夹中

using MvcDemo2.Models;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Linq;

namespace MvcDemo2.ViewComponents
{
    public class ProductListViewComponent:ViewComponent
    { 
        public async Task<IViewComponentResult> InvokeAsync(bool ischecked,string name)
        {
            var list=new List<Product>(){
                new Product(){IsChecked=true,Name="iphone x",Price=10000},
                new Product(){IsChecked=false,Name="iphone 8p",Price=6600},
            };
             var data= list.Where(m=>m.IsChecked==ischecked&&m.Name.Contains(name));
             return View(data);          
        }
        
    }
}
ProductListViewComponent

②创建视图组件 Razor 视图

 创建 Views/Shared/Components 文件夹。 此文件夹 必须 命名为 Components

@model IEnumerable<Product>

@foreach(var item in Model)
{
    <p>@item.Name</p>
}
Default.cshtml
@await Component.InvokeAsync("ProductList",new {ischecked=true,name="iphone"})
视图页面调用

 

7,上传文件

①使用模型绑定上传小文件

using System.Collections.Generic;
using Microsoft.AspNetCore.Http;

namespace MvcDemo2.Models
{
    public class FileUpViewModel
    {
        public IEnumerable<IFormFile> files {get;set;}
        public string name{get;set;}
    }
}
FileUpViewModel
public IActionResult FileUp(FileUpViewModel model)
        {
            return View();
        }
        
Controller
<form asp-action="FileUp" enctype="multipart/form-data" method="POST" role="form">
    <legend>提交</legend>

        <input type="file" name="files" class="form-control" multiple/>
        <input type="text" name="name" class="form-control"/>
    
    
    <button type="submit" class="btn btn-primary">Submit</button>
</form>
View

 

8,筛选器

每种筛选器类型都在筛选器管道中的不同阶段执行。

  • 授权筛选器最先运行,用于确定是否已针对当前请求为当前用户授权。 如果请求未获授权,它们可以让管道短路。

  • 资源筛选器是授权后最先处理请求的筛选器。 它们可以在筛选器管道的其余阶段运行之前以及管道的其余阶段完成之后运行代码。 出于性能方面的考虑,可以使用它们来实现缓存或以其他方式让筛选器管道短路。它们在模型绑定之前运行,所以可以影响模型绑定。

  • 操作筛选器可以在调用单个操作方法之前和之后立即运行代码。 它们可用于处理传入某个操作的参数以及从该操作返回的结果。

  • 异常筛选器用于在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。

  • 结果筛选器可以在执行单个操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。

下图展示了这些筛选器类型在筛选器管道中的交互方式:

①筛选器通过不同的接口定义支持同步和异步实现

using FiltersSample.Helper;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class SampleActionFilter : IActionFilter
    {
        public void OnActionExecuting(ActionExecutingContext context)
        {
            // do something before the action executes
        }

        public void OnActionExecuted(ActionExecutedContext context)
        {
            // do something after the action executes
        }
    }
}
同步实现
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class SampleAsyncActionFilter : IAsyncActionFilter
    {
        public async Task OnActionExecutionAsync(
            ActionExecutingContext context,
            ActionExecutionDelegate next)
        {
            // 在行动执行之前做一些事情
            var resultContext = await next();
            // 在执行操作后执行某些操作; resultContext.Result将被设置
        }
    }
}
异步实现

②添加为全局筛选器

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.Filters.Add(new AddHeaderAttribute("GlobalAddHeader", 
            "Result filter added to MvcOptions.Filters")); // an instance
        options.Filters.Add(typeof(SampleActionFilter)); // by type
        options.Filters.Add(new SampleGlobalActionFilter()); // an instance
    });

    services.AddScoped<AddHeaderFilterWithDi>();
}
ConfigureServices

取消和设置短路

 通过设置提供给筛选器方法的 context 参数上的 Result 属性,可以在筛选器管道的任意位置设置短路。 例如,以下资源筛选器将阻止执行管道的其余阶段

using System;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;

namespace FiltersSample.Filters
{
    public class ShortCircuitingResourceFilterAttribute : Attribute,
            IResourceFilter
    {
        public void OnResourceExecuting(ResourceExecutingContext context)
        {
            context.Result = new ContentResult()
            {
                Content = "Resource unavailable - header should not be set"
            };
        }

        public void OnResourceExecuted(ResourceExecutedContext context)
        {
        }
    }
}
ShortCircuitingResourceFilterAttribute

 

9,绑定与压缩

[
  {
    "outputFileName": "wwwroot/css/site.min.css",
    "inputFiles": [
      "wwwroot/css/site.css"
    ]
  },
  {
    "outputFileName": "wwwroot/js/site.min.js",
    "inputFiles": [
      "wwwroot/js/site.js"
    ],
    "minify": {
      "enabled": true,
      "renameLocals": true
    },
    "sourceMap": false
  }
]
bundleconfig.json
outputFileName:要输出的捆绑文件名称。可以包含中的相对路径 bundleconfig.json文件(必填)
inputFiles: 要将捆绑在一起的文件的数组。 这些是配置文件的相对路径(可选)
minify:输出类型缩减选项
sourceMap:指示是否生成捆绑的文件的源映射的标志

①需要引用: <DotNetCliToolReference Include="BundlerMinifier.Core" Version="2.6.362" />  

②执行命令:  dotnet bundle 会合并并压缩inputFiles里的文件到outputFileName

 

 

五、Model

1,数据注解

①DisplayFormat

[DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)]

Column 

[Column("FirstName")]
 public string FirstMidName { get; set; }

实体属性表字段映射

 

2,DatabaseGenerated标记主键

实体

 

 

 

六、配置

 

Vue 2.0学习笔记之Vue中的computed属性

Vue 2.0学习笔记之Vue中的computed属性

Vue中的 computed 属性称为 计算属性 。在这一节中,我们学习Vue中的计算属性如何使用?记得在学习Vue的模板相关的知识的时候,知道在模板内可以使用表达式,而且模板内的表达式是非常的便利,但这种遍历是有一定的限制的,它们实际上是用于一些简单的运算。也就是说,如果在模板中放入太多的逻辑会让模板过重而且难以维护。咱们先来看一个示例:

rush:xhtml;">
{{ message.split('').reverse().join('') }}

在这个示例中,模板不再简单和清晰。你必须看一段时间才能意识到,这里是想要显示变量 message 的翻转字符串。当你想要在模板中多次引用此处的翻转字符串时,就会更加难以处理。

这就是对于任何复杂逻辑,你都应当使用 计算属性 的原因。接下来咱们一起来学习Vue中的计算属性。

计算属性可用于快速计算视图( View )中显示的属性。这些计算将被缓存,并且只在需要时更新。

在Vue中有多种方法为视图设置值:

  • 使用指令直接将数据值绑定到视图
  • 使用简单的表达式对内容进行简单的转换
  • 使用过滤器对内容进行简单的转换

除此之外,我们还可以使用计算属性根据数据模型中的值或一组值来计算显示值。

计算属性

计算属性允许我们对指定的视图,复杂的值计算。这些值将绑定到依赖项值,只在需要时更新。

例如,我们可以在数据模型中有一个 results 数组:

rush:js;"> data () { return { results: [ { name: 'English',marks: 70 },{ name: 'Math',marks: 80 },{ name: 'History',marks: 90 } ] } }

假设我们想要查看所有主题的总数。我们不能使用 filters 或 expressions 来完成这个任务。

  • filters :用于简单的数据格式,在应用程序的多个位置都需要它
  • expressions :不允许使用流操作或其他复杂的逻辑。他们应该保持简单

这个时候,计算属性就可以派上用场。我们可以向模型中添加一个计算值,如下:

rush:js;"> computed: { totalMarks: function () { let total = 0 let me = this for (let i = 0; i < me.results.length; i++) { total += parseInt(me.results[i].marks) } return total } }

totalMarks 计算属笥使用数组 resultes 的 marks 计算出总值。它只是循环遍历值并返回子总数。

然后,我们可以在视图中显示计算值:

rush:xhtml;">
Marks for {{ subject.name }}: {{ subject.marks }}
Total marks are: {{ totalMarks }}

Vue 2.0学习笔记之使用$refs访问Vue中的DOM

Vue 2.0学习笔记之使用$refs访问Vue中的DOM

通过前面对Vue的学习,到现在我们很有必要进一步了解Vue实例中的一些特殊的属性和方法。首先要了解的是$refs属性。但是在深入到JavaScript部分之前,我们先看看模板。

rush:js;">
{{ message }}

let app = new Vue({
el: '#app',data () {
return {
message: 'Hi,大漠!'
}
},methods: {
clickedButton: function () {
console.log('Hi,大漠!')
}
}
})

在Vue的模板中,我们可以在模板中的任何元素中添加ref属性,这样就可以在Vue实例中引用这些元素。更具体地说,可以访问DOM元素。在上面的示例中的

vue(1)--指令,回流与重绘---2019.5.20学习笔记

vue(1)--指令,回流与重绘---2019.5.20学习笔记

Vue
作者:尤雨溪
框架:MVVM
渐进式的Javascript框架
框架与类库的区别?
举例:
jquery好处:1.抹平了各个浏览器之间的差异
2.链式操作DOM
套餐
框架:全家桶
渐进式:vue只会包含核心语法,如需其他功能需要单独安装,例如:路由 vuex 前后端数据交互的方式
什么是MVVM?
mvc:model view controller 大多数会用在后端
MVVM: 大多数用在前端
M:model层 数据层 数据的增删改查的操作
V:view层 视图层 类似于这个html一样的模板 主要用来做展示
VM:viewModel层 mode层与view之间的一个映射层 联系model和view层

vue最大的优点 MVVM最大的优点
数据驱动视图
1.MVC MVVM

2.传统的DOM渲染过程:
html解析器,生成dom树
css解析器,生成行间样式表

3.,回流和重绘(博客-设计模式-单例模式)
回流:只要文档布局发生了改变那么就会发生回流
重绘:当前元素对自己本身的样式进行改变,

4.虚拟DOM的理解
真是的JS对象
操作内存中的js对象要比 操作DOM节省性能


vue的副本使用
vuejs官网

5.模块

v-text:
底层原理:innerText
作用:渲染数据
v-text的指:任何js表达式
简写:{{}}

v-html:
底层原理:innerHTML
作用:渲染数据
v-html的值:任何js表达式
简写:{{{}}} 注意这个方法现在已经被淘汰了 vue1.0

v-show:显示隐藏
值:布尔值
true为显示
false为隐藏
原理:操作元素的display属性

v-if:显示隐藏
值:布尔值
true为显示
false为隐藏
原理:操作元素的DOM节点增加和删除
v-else-if
v-else
注意在使用v-else-if v-else 前面必须要有v-if

面试题:
v-if与v-show的两个区别
前者是操作元素的DOM节点创建元素和删除元素
后者是操作元素的display属性
使用场景:
单纯的元素显示隐藏,不会涉及到权限.安全.页面展示的情况下一般使用v-show
如果涉及到权限(例如vip权限).安全 .页面展示的情况下用v-if

v-for
语法:v-for="(数组中的值,数组中的下标) in 需要遍历的数据"

v-bind:绑定属性
属性:id class style src title href....
v-bind:简写:属性名称

v-on:事件的绑定
vue所需用到的一些事件函数必须放在methods中
v-on:click
v-on:dblclick
v-on:mouseover

修饰符:做事件的修饰
用法:v-on:事件mingc.修饰符.修饰符
一个事件可以有多个修饰符
.stop 阻止事件冒泡event.stopPropagation()
.prevent 组织浏览器默认事件event.preventDefault()
.once 只会触发一次回调
.enter 按下回车的时候
.13
如果需要在事件中用到事件对象,需要在事件函数中传递一个$event
.capture - 添加事件侦听器时使用 capture 模式。
.self - 只当事件是从侦听器绑定的元素本身触发时才触发回调。
.{keyCode | keyAlias} - 只当事件是从特定键触发时才触发回调。
.native - 监听组件根元素的原生事件。
.left - (2.2.0) 只当点击鼠标左键时触发。
.right - (2.2.0) 只当点击鼠标右键时触发。
.middle - (2.2.0) 只当点击鼠标中键时触发。
.passive - (2.3.0) 以 { passive: true } 模式添加侦听器

v-once:只会渲染一次数据
当数据发生改变的时候,凡是加v-once指令的标签数据不会发生改变

v-pre:不解析{{}}中的数据,包括{{}}

v-cloak:防止第一次渲染的时候{{}}的出现

v-model:实现双数据绑定
使用的元素:form表单的元素都可以使用v-model
面试题:
v-model实现双数据绑定的原理?
底层用了一个object.defineProperty() 做数据劫持 检测当前对象数据发生了变化,就会触发一个方法getter/setter
如何做到数据劫持:(vue.js中的深入响应式原理 详见:https://cn.vuejs.org/v2/guide/reactivity.html#ad)
VUE3.0中废除掉了Object.defineProperty 换成ES6的proxy() (详见:https://www.cnblogs.com/nanianqiming/p/9164097.html)

回流(reflow)和重绘(repaint)

回流:当render tree的一部分或者全部的元素因改变了自身的宽高.布局,显示或隐藏,或者元素内部的文字结构发生变化,导致需要重新构建页面的时候,回流就产生了

重绘:当一个元素自身的宽高,布局,及显示或者隐藏没有改变,而只是改变看元素的外观风格的时候,就会产生重绘.

回流必定出发重绘,而重绘不一定触发回流

触发回流的css属性:

 

触发重绘的:

 

避免回流的方法:

  1. 不使用以上能触发图层回流的属性.
  2. 建立一个图层,让回流在这些图层里面进行,限制回流和重绘的范围,减少浏览器的运算工作量

 

我们今天的关于vue2.0学习笔记vue2.0教程的分享已经告一段落,感谢您的关注,如果您想了解更多关于asp.net core2.0学习笔记、Vue 2.0学习笔记之Vue中的computed属性、Vue 2.0学习笔记之使用$refs访问Vue中的DOM、vue(1)--指令,回流与重绘---2019.5.20学习笔记的相关信息,请在本站查询。

本文标签:

上一篇android imageview 笔记(android中imageview)

下一篇Android编程权威指南笔记3:Android Fragment讲解与Android Studio中的依赖关系,如何添加依赖关系