liziyu 发布的文章

第一步,在app.json 中加入 useExtendedLib 字段

  "useExtendedLib": {
    "weui": true
  }

app.json.png

第二步,在所需要的页面的 json 文件中 加入 usingComponents 字段

  "usingComponents": {
    "mp-uploader": "weui-miniprogram/uploader/uploader",
    "mp-cells": "weui-miniprogram/cells/cells",
    "mp-cell": "weui-miniprogram/cell/cell"
  }

index.json.png

第三步,在所需要的页面中调用该组件

<mp-cells>
  <mp-cell>
    <mp-uploader bindfail="uploadError" bindsuccess="uploadSuccess" select="{{selectFile}}" upload="{{uplaodFile}}" files="{{files}}" max-count="6" title="资料上传" tips="已加密请放心上传"></mp-uploader>
  </mp-cell>
</mp-cells>

最后,最后,看一下效果图

xiaoguo.png

感谢在最后:https://blog.csdn.net/Hero_rong/article/details/107063655

这个可能只有自个能看明白了。

{field: 'start_time', title: '生效时间', templet: function(d) {
    return util.toDateString(d.start_time * 1000, 'yyyy-MM-dd HH:mm:ss')
}},

在弹窗内的

admin.open({
  success: function(mData) {
     //时间选择器
      laydate.render({
         elem: '#start_time',
         type: 'datetime',
         value: mData ? new Date(mData.start_time * 1000) : '',
      });
  }
});


最近几个月因为公司的业务需求,一直在折腾小程序,从开始的完全不熟悉,到后面被各种坑折磨,是时候写一篇总结了,避免下一次遇到还找不到解决的方案。

配置项app.json和page.json

app.json

首先,需要确定的是,app.json 是小程序的全局配置,放在根目录下。

常用的一些字段如下:

  • pages: 这个用于设置页面的路径,通常访问的页面都放在pages文件夹里面,且必须在这个全局配置文件中设置好路径,否则访问的时候会报错找不到该路径, 写在 pages 字段的第一个页面就是这个小程序的首页 (打开小程序看到的第一个页面)
  • window: 全局样式的配置
    • 导航栏相关:可配置导航栏背景颜色、标题颜色,标题内容,
    • 可以隐藏导航栏(navigationStyle设置为custom,但需要注意, 自定义导航栏仍然保留右上角胶囊,且没有返回键,如果想要实现返回功能,需要自己自定义返回的样式和功能 ),==该设置对web-view 组件无效==
    • 窗口的背景色, backgroundColor,(即小程序下拉时露出的那一截),另外,仅ios支持的还有backgroundColorTop和backgroundColorBottom, 顶部和底部窗口的背景色
    • 下拉loading的样式,只有黑色和白色(dark / light)
    • enablePullDownRefresh ,小程序默认页面是没有下拉刷新功能的,如果在app.json中配置该项为true,则所有页面都可以生效。如果只想在单个页面使用下拉刷新,需要在对应的page.json中进行配置
    • onReachBottomDistance, 上拉加载更多生效时距页面底部距离,默认为50px
    • 屏幕旋转,pageOrientation, 暂时还没有用到过,支持 auto / portrait / landscape,默认为portrait竖屏显示,
  • tabbar: tab栏设置
    • 位置:支持顶部和底部(通常使用底部tab栏), position(bottom / top)
    • tab列表 list: 文字以及对应的路径
      • 最少2个,最多5个
      • pagePath,页面路径(必须是pages中定义的路径)
      • text, tab上的文字
      • iconPath, 图片路径,( 大小限制为 40kb,建议尺寸为 81px * 81px,不支持网络图片 )
      • selectedIconPath,选中时的图片路径。限制同上
    • 样式相关:color字体颜色,selectedColor选中文字的颜色,backgroundColor是tab栏背景色 ,borderStyle上边框颜色,
    • custom是否自定义,默认为否, 具体文档参考: 自定义tabBar
  • networkTimeout(设置网络请求的超时时间)
    • request
    • connectSocket
    • uploadFile
    • downloadFile
  • debug, 开启调试,会输出Page 的注册,页面路由,数据更新,事件触发等信息,方便查看生命周期的调试
  • navigateToMiniProgramAppIdList, 跳转到其他小程序时,需要先在app.json中进行声明
  • usingComponents,如果在app.json中声明该组件,则全局可以使用该自定义组件,不需要再在page的json中声明
  • permission, 用于授权相关,平时项目中是自己写的页面,调用button的open-type属性唤起授权,其实可以通过插件功能页来实现,比如授权昵称, 用户信息功能页

page.json

页面的样式配置优先级比全局配置中的window高。

  • 可以设置导航栏背景颜色、导航栏标题颜色,导航栏标题,
  • 可以隐藏导航栏,custom 自定义导航栏,只保留右上角胶囊按钮
  • 窗口的背景色 , 以及ios支持顶部和底部窗口的背景色
  • backgroundTextStyle,下拉 loading 的样式,仅支持 dark / light
  • enablePullDownRefresh开启下拉刷新,这个属性只控制当前的page页面可以刷新
  • onReachBottomDistance,上拉加载更多生效时距页面底部距离,默认为50px
  • pageOrientation,屏幕旋转设置
  • disableScroll ,为 true 则页面整体不能上下滚动。只在页面配置中有效,无法在 app.json 中设置
  • disableSwipeBack, 禁止页面右滑手势返回
  • usingComponents, 使用自定义组件,如果没有在页面的json中声明,直接使用组件会报错

sitemap.json文件(用于微信索引)

默认规则为都默认被索引:

{"action": "allow", "page": "*"}

语法区别

wxml文件跟html的区别:

  • 没有div, p, span, 使用view代替div, text代替span
  • wx:if的使用同vue中的v-if, 但使用上略有区别,不管在标签中还是文本上,都是使用双大括号来表示插值, wx:for="{{array}}" ,
  • 条件渲染有wx:if, wx:elif, wx:else, 使用方式: <view wx:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
  • 尤其注意,如果是使用boolean类型的值,不要直接在双引号内直接写,也需要写在双大括号里面。如 <checkbox checked="{{false}}"> </checkbox>
  • 列表渲染 wx:for="{{array}}" , 可直接使用item和index,也可以使用 wx:for-item="idx" , 和 wx:for-item="itemName" 重新指定index和item的key值, 花括号和引号之间如果有空格,将最终被解析成为字符串
  • wxml文件中有一个标签是block, 仅仅是一个包装元素,不会在页面中做任何渲染,只接受控制属性
  • wx:key的值只能是字符串或者 "*this", wx:key="*this" 表示for 循环中的 item 本身, 但是要保证这个item本身是一个唯一的字符串或者数字, 当key绑定的是对象的时候会报错

问题思考?

wx:if 和 hidden 该怎么合理的判断使用场景?

  • wx:if 是惰性的,只有为true时才会局部渲染,当 wx:if 的条件值切换时,条件块会在切换时销毁或重新渲染。
  • 而hidden始终会被渲染,只是控制显示与隐藏
  • 所以,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好

组件

基础组件

  • 组件的公共属性:
    • id, class, style, hidden, data- , bind /catch*
  • 视图容器:
    • 可移动: movable-area, movable-view。 其中,movable-view必须在 movable-area 组件中,并且必须是直接子节点
    • 覆盖原生组件:cover-view, cover-image,主要用于覆盖 map、video、canvas、camera、live-player、live-pusher等级别高的原生组件。 注意,在cover-view中只能嵌套cover-view、cover-image和button
    • 可滚动的视图区域:scroll-view, 竖向滚动时,需要给scroll-view一个固定高度
    • 滑块视图容器: swiper, 其中只能放置swiper-item组件(常用于轮播图)
    • 视图容器view, 如果想使用hover的样式,可以指定按下去的样式类,跟hover相关的属性: hover-class,hover-stop-propagation, hover-start-time, hover-stay-time
  • 图标icon,在小程序中可以使用自带的一些icon图标,可以设置type、大小、颜色,详情查看, 小程序icon组件
  • 进度条,progress, 普通的进度条包括动画等可以直接使用该组件
  • 富文本,rich-txt, (可以使用该组件渲染html内容,传入html字符串)
  • 文本,text, (如果需要展示多个空格,可以设置space属性)
  • 按钮,button,(可以设置disabled属性进行禁用,防止多次点击触发事件)
  • 单选、多选框:radio、radio-goup、checkbox、checkbox-group
  • 富文本编辑器,editor
  • 表单form,支持switch input checkbox slider radio picker 的提交
  • 输入框input,支持文本、数字、身份证、带小数点的数字键盘等4个类型,password可以显示密码类型,(可以控制键盘右下角的文字,使用confirm-type属性控制,包括“发送”、“搜索”、“下一个”、“前往”、“完成”)注意:input无法设置 font-family,使用的是系统字体
  • 滚动选择器,picker,支持普通、多列、时间、日期、省市选择器,
  • 嵌入页面的滚动选择器, picker-view, picker-view-column
  • 滑动选择器,slider,类似进度条不过可以拖动
  • 开关,switch
  • 多行输入框,textarea
  • 导航,navigator,(类似于a标签,可以使用navigate、redirect、switchTab、reLaunch、navigateBack、exit(退出小程序)等类型)
  • 系统相机,camera,可以用于扫描二维码
  • 视频video,(注意,使用video组件在列表中的时候会引起小程序页面卡顿甚至崩溃,目前项目中的解决方案是使用阿里云上传视频的截帧图片来替换掉video组件,只有点击图片后才会播放视频,查看网上资料说是video的src默认为空,解决了viode自动下载的bug,使用custom-cache="{{false}}"可以解决视频缓存中卡住的问题,不过还没有试过)
  • 公众号关注组件,official-account
  • 承载网页的容器,web-view,(适用于第三方网站内容的嵌入,注意需要在公众平台配置业务域名),避免在链接中带有中文字符,在 iOS中会有打开白屏的问题,建议加一下encodeURIComponent

原生组件的使用限制

  • 原生组件包括:
    • camera
    • canvas
    • input(仅在focus时表现为原生组件)
    • live-player
    • live-pusher
    • map
    • textarea
    • video
  • 层级: 最高,其他组件无论设置 z-index 为多少,都无法盖在原生组件上
  • 原生组件不能设置动画、fixed布局、不能使用overflow: hidden来裁剪原生组件的显示区域,
  • 事件监听不能使用 bind:eventname 的写法,只支持 bindeventname,不支持 catch 和 capture 的事件绑定方式

事件系统

  • 事件处理函数,参数是event,event的事件内容包括:

    • type
    • timeStamp, 事件生成时的时间戳
    • target,触发事件的 源组件
    • currentTarget,事件绑定的 当前组件
    • detail, 自定义事件所携带的数据
    • touches,一个数组,每个元素为一个 Touch 对象
    • changedTouches, 有变化的触摸点
  • dataset:在target或currentTarget的事件对象中,可以通过dataset获取当前自定义的一些数据,(在JS文件中使用event.currentTarget.dataset时,dataset的属性会自动转换字符串, 连字符写法会转换成驼峰写法,而大写字符会自动转成小写字符 )

  • mark,类似于dataset,(如果存在同名的 mark ,父节点的 mark 会被子节点覆盖)

  • 页面的用户行为:

    • 下拉刷新 onPullDownRefresh
    • 上拉触底 onReachBottom
    • 页面滚动 onPageScroll
    • 用户转发 onShareAppMessage

思考:

  1. 小程序中bind事件和catch事件有什么区别?
  • bind事件绑定不会阻止冒泡事件向上冒泡,
  • catch事件绑定可以阻止冒泡事件向上冒泡
  1. target和currentTarget有什么不同?
<view id="outer" bindtap="handleTap1">
  outer view
  <view id="middle" catchtap="handleTap2">
    middle view
    <view id="inner" bindtap="handleTap3">
      inner view
    </view>
  </view>
</view>
复制代码

如上面这个示例中,点击inner view会触发handleTap3,同时会冒泡到handleTap2。 对于handleTap2这个组件来说,此时收到事件对象的target就是inner的元素,而currentTarget是middle的部分

  1. mark 和 dataset 有什么区别?
  • mark 会包含从触发事件的节点到根节点上所有的 mark: 属性值;
  • dataset 仅包含一个节点的 data- 属性值
  • 节点的 mark 不会做连字符和大小写转换

小程序相关机制

  • 小程序框架的逻辑层并非运行在浏览器中,因此 JavaScript 在 web 中一些能力都无法使用,如 window,document 等

小程序的运行机制:冷启动和热启动

  • 冷启动: 首次打开小程序;或小程序销毁后打开。(冷启动会触发app.js中的onLaunch生命周期函数)
  • 热启动: 点击右上角胶囊退出、或者home键离开微信、或者左右滑动返回到微信等动作,都是热启动,并没有销毁小程序,(所以热启动不会触发onLaunch函数,但会触发onShow函数)

启动场景

  • 小程序启动场景分为两种:
    • 从发现栏、另一个小程序返回、微信支付、首页下拉小程序栏等为A场景
    • B场景:打开某个特定页面:如转发分享的卡片链接
  • 热启动场景效果:
    1. 如果重新进入的场景和退出时的场景都是A场景,则保留原来状态;例如:停留在“个人中心”页面,退出后,下拉首页的小程序列表进入后还是会停留在“个人中心”页面,(触发“个人中心”Page的onShow函数,但是不会触发onLoad函数)
    2. 如果当前是A场景,上一个是B场景,则清空原来的页面栈,打开首页(即执行 wx.reLaunch 到首页)如分享出去的卡片是“学员列表”页面,点击卡片停留后退出,再从首页下拉的小程序进入,此时会跳转到首页
    3. 不管上一个场景是什么,如果当前是B场景,如点击小程序的分享卡片,此时会清空原来的页面栈,重新进入这个分享页,首先触发app中的onShow函数,然后触发这个分享页的onLoad函数,当然如果app中已经进行了跳转到其它页面,则不会再走这个分享页。
  • 冷启动场景效果:
    • 冷启动规则比较简单,如果冷启动时是A场景,则进入小程序首页
    • 如果冷启动时是B场景,则进入对应的特定页面

更新机制

  • 小程序首先会在后台有一个最新版本,一般来说会在24小时内下发新版本信息到用户,静默更新到新版本(此时用户是无感知态)
  • 在后台还没及时更新版本时,每次冷启动都会检测是否有更新版本,如果有,会 异步下载 ,同时启动当前的旧版本包,所以新版本需要下一次的冷启动才会应用上
  • 如果启动时就想立刻获取最新版本,需要使用 wx.getUpdateManager 的API,用wx.showModal提示用户是否重启应用更新

ES6的支持情况:

  • 微信小程序已经支持了绝大部分的 ES6 API,但部分API仍依赖于系统版本而不支持
  • String的normalize在ios8,ios9不支持
  • 数组的values在ios8和android不支持
  • 数组的includes在ios8不支持
  • Proxy不支持ios8、ios9和android

注册小程序

  • app.js中注册小程序,整个小程序只有一个 App 实例,是全部页面共享的。
  • 开发者可以通过 getApp 方法获取到全局唯一的 App 示例,获取App上的数据或调用开发者注册在 App 上的函数
  • 在app.js中可以直接使用this.globalData赋值,但在其他页面需通过getApp()方法获取到实例后才能使用全局变量globalData
App({
  onLaunch: function(options) {},
  onShow: function(options) {},
  onHide: function() {},
  onError: function(msg) {},
  globalData: 'I am global data'
})
复制代码

注册页面和自定义组件

页面

  • data 是页面第一次渲染使用的初始数据
  • onLoad页面加载时触发。一个页面只会调用一次;
  • onShow在页面显示/切入前台时触发
  • onPageScroll,监听用户滑动页面事件,( 请避免在 onPageScroll 中过于频繁的执行 setData 等引起逻辑层-渲染层通信的操作。尤其是每次传输大量数据,会影响通信耗时 )
  • onShareAppMessage监听页面内的转发,转发有button 组件 open-type="share"和右上角转发两个方式,(只有Page中定义了onShareAppMessage,右上角才会出现转发功能)
  • setData:
    • 将数据从逻辑层发送到视图层(异步),同时改变对应的 this.data 的值(同步)(跟react的setState类似,但又不一样)
    • 直接修改 this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致
    • 不要把 data 中任何一项的 value 设为 undefined
    • 如果需要视图层更新完毕后再处理事件,可以将事件放在setData的回调函数中

Page构造器:

Page({
  data: { text: "This is page data." },
  onLoad: function(options) { },
  onReady: function() { },
  onShow: function() { },
  onHide: function() { },
  onUnload: function() { },
  onPullDownRefresh: function() { },
  onReachBottom: function() { },
  onShareAppMessage: function () { },
  onPageScroll: function() { }
})
复制代码

自定义组件

  • 页面使用自定义组件时需要现在page的json文件中声明usingComponents定义段
  • 组件使用页面的生命周期方法时(即 on 开头的方法),应该写在methods定义段中
  • behaviors 提取所有页面中公用的代码段,例如, 在所有页面被创建和销毁时都要执行同一段代码,就可以把这段代码提取到 behaviors 中
// page-common-behavior.js
module.exports = Behavior({
  attached: function() {
    // 页面创建时执行
    console.info('Page loaded!')
  },
  detached: function() {
    // 页面销毁时执行
    console.info('Page unloaded!')
  }
})


// 页面 A
var pageCommonBehavior = require('./page-common-behavior')
Component({
  behaviors: [pageCommonBehavior],
  data: { /* ... */ },
  methods: { /* ... */ },
})


// 页面 B
var pageCommonBehavior = require('./page-common-behavior')
Component({
  behaviors: [pageCommonBehavior],
  data: { /* ... */ },
  methods: { /* ... */ },
})
复制代码
  • 在 properties 定义段中,属性名采用驼峰写法(propertyName);在 wxml 中,指定属性值时则对应使用连字符写法(component-tag-name property-name="attr value"),应用于数据绑定时采用驼峰写法(attr="")
  • observers数据监听器,(类似于vue中的watch),具体请参考 数据监听器
  • created,组件实例刚被创建,( 注意此时不能调用 setData )
  • definitionFilter,定义段过滤器,用于自定义组件扩展(如扩展自定义组件一个computed计算属性功能)

注意:

  • 使用 this.data 可以获取内部数据和属性值;但直接修改它不会将变更应用到界面上,应使用 setData 修改
  • 生命周期函数无法在组件方法中通过 this 访问到

组件间的通信与事件

  • 父传子:父组件绑定属性值,子组件通过properties获取,
  • 子组件向父组件传递数据:使用事件传递
  • 父组件访问子组件: 父组件可以通过 this.selectComponent 方法获取子组件实例对象,这样就可以直接访问组件的任意数据和方法

此处列举第二种自定义事件传递数据的方法:

// 1. 父组件中监听事件
<component-tag-name bindmyevent="onMyEvent" />

// 2. 子组件触发事件,触发wxml的onMyEvent事件,触发父组件的方法
<!--自定义组件中-->
<button bindtap="onTap">点击这个按钮将触发“myevent”事件</button>

<!--自定义组件js文件中-->
Component({
  properties: {},
  methods: {
    onTap: function(){
      var myEventDetail = {} // detail对象,提供给事件监听函数
      var myEventOption = {} // 触发事件的选项
      this.triggerEvent('myevent', myEventDetail, myEventOption)
    }
  }
})

复制代码

具体可参考文档: 组件间通信与事件

注意:

  • 组件和引用组件的页面不能使用id选择器(#a)、属性选择器([a])和标签名选择器,请改用class选择器
  • 避免使用后代选择器(.a .b)
  • 子元素选择器(.a>.b)只能用于 view 组件与其子节点之间,用于其他组件可能导致非预期的情况
  • app.wxss 中的样式、组件所在页面的的样式对自定义组件无效(除继承样式外,如 font 、 color会继承自组件外)

组件间关系

场景: 如果一个页面中需要使用到A组件和B组件,而A组件与B组件之间需要通信,此时可以在组件定义时加入relations定义段,如封装的ul和li组件后,之间需要存在关联的父子关系

  • 必须在两个组件定义中都加入relations定义,否则不会生效
  • type,与目标组件的相对关系,可选的值为 parent 、 child 、 ancestor 、 descendant
  • 另一种情况是关联一类组件(如form与input、checkbox等的关系),需要使用到behavior

具体使用方式参见 组件间关系

使用setData应注意的事项:

  • 不要频繁的去 setData,否则可能导致Android 下用户在滑动时会感觉到卡顿,或者渲染有出现延时
  • 不要每次 setData 都传递大量新数据
  • 不要在后台态页面进行 setData ,

小程序运行流程

  1. 微信打开小程序前,会把整个小程序的代码包下载到本地
  2. 通过 app.json 的 pages 字段就可以知道你当前小程序的所有页面路径
  3. 写在 pages 字段的第一个页面就是这个小程序的首页(打开小程序看到的第一个页面),所以微信会把首页的代码装载进来
  4. 小程序启动,触发app.js 定义的 App 实例的 onLaunch回调,启动时也会触发onShow函数,或切前台、从其他页面返回到这个页面,都会触发onShow;onHide监听小程序切后台,(如tab切换,navigateTo,离开微信等); onPageNotFound页面不存在监听函数
  5. 页面的渲染流程开始,微信客户端会先根据 page的json文件配置生成一个界面,
  6. 接着装载这个页面的 WXML 结构和 WXSS 样式
  7. 最后客户端会装载 page的js文件
  8. Page构造器会生成一个页面,生成页面的时候小程序框架会把 data 数据和 index.wxml 一起渲染出最终的结构,渲染完界面
  9. 渲染完界面之后,页面实例就会收到一个 onLoad 的回调

生命周期函数

小程序里面又三种生命周期:小程序运行的生命周期(在app.js中处理),页面的生命周期(在page的js文件中处理,或者组件的pageLifetimes触发的生命周期函数),组件的生命周期

小程序生命周期函数

  • onLaunch, 小程序初始化完成时触发,全局只触发一次
  • onShow,小程序启动,或从后台进入前台显示时触发
  • onHide,从前台进入后台时触发
  • onError,小程序发生脚本错误或 API 调用报错时触发
  • onPageNotFound,小程序要打开的页面不存在时触发

onLaunch,onShow参数:

  • path: 打开小程序的页面路径
  • query: 打开小程序的页面参数query
  • scene: 打开小程序的场景值,
  • shareTicket: 转发分享参数
  • referrerInfo:当场景为由从另一个小程序或公众号或App打开时,返回此字段
  • referrerInfo.appId: 来源小程序或公众号或App的 appId
  • referrerInfo.extraData: 来源小程序传过来的数据,scene=1037或1038时支持

页面生命周期函数

  • onLoad,监听页面加载
  • onShow, 监听页面显示(如tab切换或者离开微信、或navigateTo)
  • onReady, 监听页面初次渲染完成(一个页面只会调用一次,代表页面已经准备妥当)
  • onHide, 监听页面隐藏, (如 wx.navigateTo 或底部 tab 切换到其他页面,小程序切入后台等)
  • onUnload, 监听页面卸载(如wx.redirectTo或wx.navigateBack到其他页面时)

组件生命周期函数

  • created,在组件实例刚刚被创建时执行,注意此时不能调用 setData
  • attached,在组件实例进入页面节点树时执行
  • ready,在组件布局完成后执行
  • moved, 在组件实例被移动到节点树另一个位置时执行
  • detached, 在组件实例被从页面节点树移除时执行

注意:

组件中存在一个组件所在页面的生命周期,定义在pageLifetimes中,其中可用的生命周期包括:

  • show, 组件所在的页面被展示时执行
  • hide, 组件所在的页面被隐藏时执行
  • resize, 组件所在的页面尺寸变化时执行

网络

  1. 服务器域名配置,当使用体验版或者开发版报“服务器开小差”了的错误,大部分可能性都是因为没有打开调试,因为在调试模式下,小程序不会去校验域名的合法性。
  2. 小程序只可以跟指定的域名与进行网络通信,包括普通 HTTPS 请求(wx.request)、上传文件(wx.uploadFile)、下载文件(wx.downloadFile) 和 WebSocket 通信(wx.connectSocket)
  3. 如果接入第三方网页,使用web-view组件,需要配置业务域名
  4. 默认超时时间为60s,超时时间可以在 app.json 或 game.json 中通过 networktimeout 配置
  5. 并发限制:wx.request、wx.uploadFile、wx.downloadFile 的最大并发限制是 10 个;wx.connectSockt 的最大并发限制是 5 个
  6. 返回状态,只要成功接收到服务器返回, 无论 statusCode 是多少,都会进入 success 回调

自定义tabBar

用户可以通过配置app.json 中的 tabBar 项指定 custom 字段为true,来自定义tabBar,不过有些使用地方还需要注意:

  1. tabBar 的相关配置项仍然需完整声明,以兼容低版本以及区分哪些是tab页
  2. 需要用户自定义一个组件来渲染 tabBar,推荐用 fixed 在底部的 cover-view + cover-image 组件渲染样式,以保证 tabBar 层级相对较高
  3. 与 tabBar 样式相关的接口,如 wx.setTabBarItem 等将失效
  4. 可以在自定义组件下通过 getTabBar 接口,获取当前页面的自定义 tabBar 组件实例

小程序登录流程

小程序登录需要调用微信的开放接口,整体流程如下:

  1. 小程序通过wx.login()获取一个code
  2. 调用后端的api,发送code,后端通过这个code来校验接口,并返回一个自定义登录态
  3. 小程序将登录状态存入storage
  4. 之后再访问服务器发送业务请求就只用后端判断登录态查询openid和session_key返回业务数据

小程序优化操作

  1. 缩短白屏时间:
    • 首屏渲染的内容较多,需要集合多份数据进行渲染:此时可以把优先级高的内容做优先展示
    • 首屏内容依赖的数据从服务端请求的时间太长:分析数据返回的时间长的原因
    • 一次性渲染数据太大或依赖的计算过于复杂:减少渲染的数据量、优化渲染相关数据的算法
  2. 渲染界面的耗时过长,需要校验下是否同时渲染的区域太大(例如列表过长)
  3. 脚本执行时间过长:需要确认并优化脚本的逻辑
  4. setData调用频繁:避免无用的频繁调用,每秒调用setData的次数不超过 20 次,
// 不要频繁调用setData
    this.setData({ a: 1 })
    this.setData({ b: 2 })
// 绝大多数时候可优化为
    this.setData({ a: 1, b: 2 })
复制代码
  1. setData的数据太大,setData的数据在JSON.stringify后不超过 256KB
  2. setData一个未绑定的变量
// 不要设置不在界面渲染时使用的数据,并将界面无关的数据放在data外
    this.setData({
      myData: {
        a: '这个字符串在WXML中用到了',
        b: '这个字符串未在WXML中用到,而且它很长…………………………'
      }
    })
    // 可以优化为
    this.setData({
      'myData.a': '这个字符串在WXML中用到了'
    })
    this._myData = {
      b: '这个字符串未在WXML中用到,而且它很长…………………………'
    }

复制代码
  1. 开启 HTTP 缓存控制
  2. 控制WXML节点数
<view data-my-data="{{myData}}"> <!-- 这个 view 和下一行的 view 可以合并 -->
  <view class="my-class" data-my-data="{{myData}}" bindtap="onTap">
    <text> <!-- 这个 text 通常是没必要的 -->
      {{myText}}
    </text>
  </view>
</view>

<!-- 可以简化为 -->

<view class="my-class" data-my-data="{{myData}}" bindtap="onTap">
  {{myText}}
</view>
复制代码
  1. 控制图片大小,图片宽高都不超过实际显示宽高的3倍
  2. 合理控制网络请求数量
  3. 控制图片请求数
  4. 开启惯性滚动:wxss中带有overflow: scroll的元素,在 iOS 下需要设置-webkit-overflow-scrolling: touch样式
  5. 避免使用:active伪类来实现点击态
  6. 保持图片大小比例
  7. 可点击元素的宽高都不小于 20px
  8. iphoneX兼容,用以下wxss进行兼容:
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
复制代码
  1. 避免JS异常
  2. 不使用废弃接口
  3. 设置最低基础库版本
  4. 移除不可访问到的页面
  5. 移除大量未使用的样式
  6. 及时回收定时器:定时器是全局的,并不是跟页面绑定的,当小程序从一个页面路由到另一个页面之后,前一个页面定时器应注意手动回收


转载:https://www.eyoucms.com/wxmini/doc/course/25179.html

QQ20221008-000133@2x.png

内核:在一些系统中,当系统调用发生时,操作系统或者操作系统内核会编程应用程序内存的一部分。

栈:栈中包含活动记录,其中包含当前活动函数调用的返回地址和局部变量等信息。

共享库:为了动态链接共享库文件而创建的一个内存片段

堆内存:被用作堆内存来使用和分配的一块内存空间。如果运行时需要一些可变大小的小内存块,那么这些内存就是从这个区域中分配的

未初始化的数据: 没有初始化的全局变量被放在固定地址中。通常,这段区域都会被初始化为0。

初始化的数据: 任何被赋予了初始值的数据都被组织在内存中的一段连续的区域内,因此他们就能够与程序页一同被有效地载入

程序页:构成应用程序的机器代码指令就是程序页。当有硬件支持时,程序页通常是只读的,这样就可以防止程序意外的重写其自身指令区域。

第0页:通常,一个以地址0为开始的内存片段会被保留下来被设置为不可读区域,他可以用来捕捉一种常见的错误,这种错误就是使用一个NULL指针访问数据。

第一部分 C++内存地址分配简介

1 内存地址是从高地址到低地址进行分配的:

int i=1;
int j=1;
cout
2 函数参数列表的存放方式是,先对最右边的形参分配地址,后对最左边的形参分配地址。

3 Little-endian模式的CPU对操作数的存放方式是从低字节到高字节的

0x1234的存放方式入下:

0X4000 0x34

0X4001 0x12

4 Big-endian模式的CPU对操作数的存放方式是从高字节到低字节的

0x1234的存放方式入下:

0x4000 0x12

0x4001 0x34

5 联合体union的存放顺序是所有成员都从低地址开始存放。

6 一个变量的地址是由它所占内存空间中的最低位地址表示的。

0X4000 0x34

0X4001 0x12

0x1234 的地址位0x4000

7 堆栈的分配方式是从高内存地址向低内存地址分配的。

int ivar=0;

int iarray[2]={11, 22};

注意iarray[2]越界使用,比如对其赋值

iarray[2]=0;

那么则同时对ivar赋值为0,可能产生死循环,因为它们的地址相同,即&ivar等于&iarray[2]。

第二部分 C/C++内存区划分

一 在C中分为这几个存储区

1.栈 由编译器自动分配释放;

2.堆 一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收;

3.全局区(静态区),全局变量和静态变量的存储是放在一块的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域。 程序结束释放;

4.另外还有一个专门放常量的地方。 程序结束释放。

在函数体中定义的变量通常是在栈上,用malloc, calloc, realloc等分配内存的函数分配得到的就是在堆上。在所有函数体外定义的是全局量,加了static修饰符后不管在哪里都存放在全局区(静态区),在所有函数体外定义的static变量表示在该文件中有效,不能extern到别的文件用,在函数体内定义的static表示只在该函数体内有效。另外,函数中的”adgfdf”这样的字符串存放在常量区。比如:

int  a = 0; //全局初始化区  
char *p1; //全局未初始化区  
void main()  
{       
int b;                   //栈       
char s[] = "abc"; //栈       
char *p2;         //栈       
char *p3 = "123456";       //123456{post.content}在常量区,p3在栈上       
static int c = 0;          //全局(静态)初始化区       
p1 = (char *)malloc(10);   //分配得来得10字节的区域在堆区       
p2 = (char *)malloc(20);   //分配得来得20字节的区域在堆区       
strcpy(p1, "123456");      
//123456{post.content}放在常量区,编译器可能会将它与p3所指向的"123456"优化成一块  
}  

二.在C++中,内存分成5个区

他们分别是堆、栈、自由存储区、全局/静态存储区和常量存储区

1.栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区。里面的变量通常是局部变量、函数参数等。

2.堆,就是那些由new分配的内存块,他们的释放编译器不去管,由我们的应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放掉,那么在程序结束后,操作系统会自动回收。

3.自由存储区,就是那些由malloc等分配的内存块,他和堆是十分相似的,不过它是用free来结束自己的生命的。

4.全局/静态存储区,全局变量和静态变量被分配到同一块内存中,在以前的C语言中,全局变量又分为初始化的和未初始化的,在C++里面没有这个区分了,他们共同占用同一块内存区。

5.常量存储区,这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改)。

在bbs上,堆与栈的区分问题,似乎是一个永恒的话题。   

首先,我们举一个例子:

void f()
{
int* p=new int[5];
}
这条短短的一句话就包含了堆与栈,看到new,我们首先就应该想到,我们分配了一块堆内存,那么指针p呢?它分配的是一块栈内存,所以这句话的意思就是:在栈内存中存放了一个指向一块堆内存的指针p。

在程序会先确定在堆中分配内存的大小,然后调用operator new分配内存,然后返回这块内存的首地址,放入栈中,他在VC6下的汇编代码如下:

00401028 push 14h
0040102A call operator new (00401060)
0040102F add esp,4
00401032 mov dword ptr [ebp-8],eax 这里写代码片
00401035 mov eax,dword ptr [ebp-8]
00401038 mov dword ptr [ebp-4],eax
这里,我们为了简单并没有释放内存,那么该怎么去释放呢?是delete p么?错了,应该是delete []p,这是为了告诉编译器:我删除的是一个数组,VC6就会根据相应的Cookie信息去进行释放内存的工作。

好了,我们回到我们的主题:堆和栈究竟有什么区别?

主要的区别由以下几点:

1、管理方式不同;

2、空间大小不同;

3、能否产生碎片不同;

4、生长方向不同;

5、分配方式不同;

6、分配效率不同;

管理方式:对于栈来讲,是由编译器自动管理,无需我们手工控制;对于堆来说,释放工作由程序员控制,容易产生memory leak。

空间大小:一般来讲在32位系统下,堆内存可以达到4G的空间,从这个角度来看堆内存几乎是没有什么限制的。但是对于栈来讲,一般都是有一定的空间大小的,例如,在VC6下面,默认的栈空间大小是1M(好像是,记不清楚了)。当然,我们可以修改:

打开工程,依次操作菜单如下:Project->Setting->Link,在Category 中选中Output,然后在Reserve中设定堆栈的最大值和commit。

注意:Reserve最小值为4Byte;commit是保留在虚拟内存的页文件里面,它设置的较大会使栈开辟较大的值,可能增加内存的开销和启动时间。

碎片问题:对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。对于栈来讲,则不会存在这个问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出,在它弹出之前,在它上面的后进的栈内容已经被弹出,详细的可以参考数据结构,这里我们就不再一一讨论了。

生长方向:对于堆来讲,生长方向是向上的,也就是向着内存地址增加的方向;对于栈来讲,它的生长方向是向下的,是向着内存地址减小的方向增长。

分配方式:堆都是动态分配的,没有静态分配的堆。栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由alloca函数进行分配,但是栈的动态分配和堆是不同的,它的动态分配是由编译器进行释放,不需要我们手工实现。

分配效率:栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的效率比较高(我的注释:关于EBP寄存器请参考另一篇文章)。

堆则是C/C++函数库提供的,它的机制是很复杂的,例如为了分配一块内存,库函数会按照一定的算法

(具体的算法可以参考数据结构/操作系统)在堆内存中搜索可用的足够大小的空间,如果没有足够大小的空间

(可能是由于内存碎片太多),就有可能调用系统功能去增加程序数据段的内存空间,这样就有机会分到足够大小的内存,

然后进行返回。显然,堆的效率比栈要低得多。

从这里我们可以看到,堆和栈相比,由于大量new/delete的使用,容易造成大量的内存碎片;由于没有专门的系统支持,效率很低;由于可能引发用户态和核心态的切换,内存的申请,代价变得更加昂贵。所以栈在程序中是应用最广泛的,就算是函数的调用也利用栈去完成,函数调用过程中的参数,返回地址,EBP和局部变量都采用栈的方式存放。所以,我们推荐大家尽量用栈,而不是用堆。

虽然栈有如此众多的好处,但是由于和堆相比不是那么灵活,有时候分配大量的内存空间,还是用堆好一些。

无论是堆还是栈,都要防止越界现象的发生(除非你是故意使其越界),因为越界的结果要么是程序崩溃,要么是摧毁程序的堆、栈结构,产生意想不到的结果,就算是在你的程序运行过程中,没有发生上面的问题,你还是要小心,说不定什么时候就崩掉,那时候debug可是相当困难的:)

转自:https://www.2cto.com/kf/201802/718986.html

这个需求先后尝试了几个aes.js包,先后不尽如意,可能水平有限没有调通。准确的说,前后端没有调通,下面这种方法亲测有效,下面记录并分享给需要的人:

PHP代码:

$key = 'aaaaaaaaaaaaaaaa'; //16位,这个长度需要与前端匹配
$iv = 'bbbbbbbbbbbbbbbb'; //16位,这个需要与前端吻合

if(!function_exists('aes_encrypt')) {
    /**
     * 加密并转BASE64 AES-128-CBC
     * @param $data
     * @return string
     */
    function aes_encrypt($data): string
    {
        return base64_encode(openssl_encrypt($data, 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv));
    }
}

if(!function_exists('aes_decrypt')) {
    /**
     * 解密并转BASE64 AES-128-CBC
     * @param $data
     * @return string
     */
    function aes_decrypt($data): string
    {
        return openssl_decrypt(base64_decode($data), 'AES-128-CBC', $key, OPENSSL_RAW_DATA, $iv);
    }
}



前端js部分

第一部分eas.js

https://github.com/kk196110/wechat/blob/master/utils/aes.js

考虑到有许多网友打开github困难,所以我把它直接拷贝下来了,如下:

/*
CryptoJS v3.1.2
code.google.com/p/crypto-js
(c) 2009-2013 by Jeff Mott. All rights reserved.
code.google.com/p/crypto-js/wiki/License
*/
var CryptoJS=CryptoJS||function(u,p){var d={},l=d.lib={},s=function(){},t=l.Base={extend:function(a){s.prototype=this;var c=new s;a&&c.mixIn(a);c.hasOwnProperty("init")||(c.init=function(){c.$super.init.apply(this,arguments)});c.init.prototype=c;c.$super=this;return c},create:function(){var a=this.extend();a.init.apply(a,arguments);return a},init:function(){},mixIn:function(a){for(var c in a)a.hasOwnProperty(c)&&(this[c]=a[c]);a.hasOwnProperty("toString")&&(this.toString=a.toString)},clone:function(){return this.init.prototype.extend(this)}},
r=l.WordArray=t.extend({init:function(a,c){a=this.words=a||[];this.sigBytes=c!=p?c:4*a.length},toString:function(a){return(a||v).stringify(this)},concat:function(a){var c=this.words,e=a.words,j=this.sigBytes;a=a.sigBytes;this.clamp();if(j%4)for(var k=0;k<a;k++)c[j+k>>>2]|=(e[k>>>2]>>>24-8*(k%4)&255)<<24-8*((j+k)%4);else if(65535<e.length)for(k=0;k<a;k+=4)c[j+k>>>2]=e[k>>>2];else c.push.apply(c,e);this.sigBytes+=a;return this},clamp:function(){var a=this.words,c=this.sigBytes;a[c>>>2]&=4294967295<<
32-8*(c%4);a.length=u.ceil(c/4)},clone:function(){var a=t.clone.call(this);a.words=this.words.slice(0);return a},random:function(a){for(var c=[],e=0;e<a;e+=4)c.push(4294967296*u.random()|0);return new r.init(c,a)}}),w=d.enc={},v=w.Hex={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++){var k=c[j>>>2]>>>24-8*(j%4)&255;e.push((k>>>4).toString(16));e.push((k&15).toString(16))}return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j+=2)e[j>>>3]|=parseInt(a.substr(j,
2),16)<<24-4*(j%8);return new r.init(e,c/2)}},b=w.Latin1={stringify:function(a){var c=a.words;a=a.sigBytes;for(var e=[],j=0;j<a;j++)e.push(String.fromCharCode(c[j>>>2]>>>24-8*(j%4)&255));return e.join("")},parse:function(a){for(var c=a.length,e=[],j=0;j<c;j++)e[j>>>2]|=(a.charCodeAt(j)&255)<<24-8*(j%4);return new r.init(e,c)}},x=w.Utf8={stringify:function(a){try{return decodeURIComponent(escape(b.stringify(a)))}catch(c){throw Error("Malformed UTF-8 data");}},parse:function(a){return b.parse(unescape(encodeURIComponent(a)))}},
q=l.BufferedBlockAlgorithm=t.extend({reset:function(){this._data=new r.init;this._nDataBytes=0},_append:function(a){"string"==typeof a&&(a=x.parse(a));this._data.concat(a);this._nDataBytes+=a.sigBytes},_process:function(a){var c=this._data,e=c.words,j=c.sigBytes,k=this.blockSize,b=j/(4*k),b=a?u.ceil(b):u.max((b|0)-this._minBufferSize,0);a=b*k;j=u.min(4*a,j);if(a){for(var q=0;q<a;q+=k)this._doProcessBlock(e,q);q=e.splice(0,a);c.sigBytes-=j}return new r.init(q,j)},clone:function(){var a=t.clone.call(this);
a._data=this._data.clone();return a},_minBufferSize:0});l.Hasher=q.extend({cfg:t.extend(),init:function(a){this.cfg=this.cfg.extend(a);this.reset()},reset:function(){q.reset.call(this);this._doReset()},update:function(a){this._append(a);this._process();return this},finalize:function(a){a&&this._append(a);return this._doFinalize()},blockSize:16,_createHelper:function(a){return function(b,e){return(new a.init(e)).finalize(b)}},_createHmacHelper:function(a){return function(b,e){return(new n.HMAC.init(a,
e)).finalize(b)}}});var n=d.algo={};return d}(Math);
(function(){var u=CryptoJS,p=u.lib.WordArray;u.enc.Base64={stringify:function(d){var l=d.words,p=d.sigBytes,t=this._map;d.clamp();d=[];for(var r=0;r<p;r+=3)for(var w=(l[r>>>2]>>>24-8*(r%4)&255)<<16|(l[r+1>>>2]>>>24-8*((r+1)%4)&255)<<8|l[r+2>>>2]>>>24-8*((r+2)%4)&255,v=0;4>v&&r+0.75*v<p;v++)d.push(t.charAt(w>>>6*(3-v)&63));if(l=t.charAt(64))for(;d.length%4;)d.push(l);return d.join("")},parse:function(d){var l=d.length,s=this._map,t=s.charAt(64);t&&(t=d.indexOf(t),-1!=t&&(l=t));for(var t=[],r=0,w=0;w<
l;w++)if(w%4){var v=s.indexOf(d.charAt(w-1))<<2*(w%4),b=s.indexOf(d.charAt(w))>>>6-2*(w%4);t[r>>>2]|=(v|b)<<24-8*(r%4);r++}return p.create(t,r)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="}})();
(function(u){function p(b,n,a,c,e,j,k){b=b+(n&a|~n&c)+e+k;return(b<<j|b>>>32-j)+n}function d(b,n,a,c,e,j,k){b=b+(n&c|a&~c)+e+k;return(b<<j|b>>>32-j)+n}function l(b,n,a,c,e,j,k){b=b+(n^a^c)+e+k;return(b<<j|b>>>32-j)+n}function s(b,n,a,c,e,j,k){b=b+(a^(n|~c))+e+k;return(b<<j|b>>>32-j)+n}for(var t=CryptoJS,r=t.lib,w=r.WordArray,v=r.Hasher,r=t.algo,b=[],x=0;64>x;x++)b[x]=4294967296*u.abs(u.sin(x+1))|0;r=r.MD5=v.extend({_doReset:function(){this._hash=new w.init([1732584193,4023233417,2562383102,271733878])},
_doProcessBlock:function(q,n){for(var a=0;16>a;a++){var c=n+a,e=q[c];q[c]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360}var a=this._hash.words,c=q[n+0],e=q[n+1],j=q[n+2],k=q[n+3],z=q[n+4],r=q[n+5],t=q[n+6],w=q[n+7],v=q[n+8],A=q[n+9],B=q[n+10],C=q[n+11],u=q[n+12],D=q[n+13],E=q[n+14],x=q[n+15],f=a[0],m=a[1],g=a[2],h=a[3],f=p(f,m,g,h,c,7,b[0]),h=p(h,f,m,g,e,12,b[1]),g=p(g,h,f,m,j,17,b[2]),m=p(m,g,h,f,k,22,b[3]),f=p(f,m,g,h,z,7,b[4]),h=p(h,f,m,g,r,12,b[5]),g=p(g,h,f,m,t,17,b[6]),m=p(m,g,h,f,w,22,b[7]),
f=p(f,m,g,h,v,7,b[8]),h=p(h,f,m,g,A,12,b[9]),g=p(g,h,f,m,B,17,b[10]),m=p(m,g,h,f,C,22,b[11]),f=p(f,m,g,h,u,7,b[12]),h=p(h,f,m,g,D,12,b[13]),g=p(g,h,f,m,E,17,b[14]),m=p(m,g,h,f,x,22,b[15]),f=d(f,m,g,h,e,5,b[16]),h=d(h,f,m,g,t,9,b[17]),g=d(g,h,f,m,C,14,b[18]),m=d(m,g,h,f,c,20,b[19]),f=d(f,m,g,h,r,5,b[20]),h=d(h,f,m,g,B,9,b[21]),g=d(g,h,f,m,x,14,b[22]),m=d(m,g,h,f,z,20,b[23]),f=d(f,m,g,h,A,5,b[24]),h=d(h,f,m,g,E,9,b[25]),g=d(g,h,f,m,k,14,b[26]),m=d(m,g,h,f,v,20,b[27]),f=d(f,m,g,h,D,5,b[28]),h=d(h,f,
m,g,j,9,b[29]),g=d(g,h,f,m,w,14,b[30]),m=d(m,g,h,f,u,20,b[31]),f=l(f,m,g,h,r,4,b[32]),h=l(h,f,m,g,v,11,b[33]),g=l(g,h,f,m,C,16,b[34]),m=l(m,g,h,f,E,23,b[35]),f=l(f,m,g,h,e,4,b[36]),h=l(h,f,m,g,z,11,b[37]),g=l(g,h,f,m,w,16,b[38]),m=l(m,g,h,f,B,23,b[39]),f=l(f,m,g,h,D,4,b[40]),h=l(h,f,m,g,c,11,b[41]),g=l(g,h,f,m,k,16,b[42]),m=l(m,g,h,f,t,23,b[43]),f=l(f,m,g,h,A,4,b[44]),h=l(h,f,m,g,u,11,b[45]),g=l(g,h,f,m,x,16,b[46]),m=l(m,g,h,f,j,23,b[47]),f=s(f,m,g,h,c,6,b[48]),h=s(h,f,m,g,w,10,b[49]),g=s(g,h,f,m,
E,15,b[50]),m=s(m,g,h,f,r,21,b[51]),f=s(f,m,g,h,u,6,b[52]),h=s(h,f,m,g,k,10,b[53]),g=s(g,h,f,m,B,15,b[54]),m=s(m,g,h,f,e,21,b[55]),f=s(f,m,g,h,v,6,b[56]),h=s(h,f,m,g,x,10,b[57]),g=s(g,h,f,m,t,15,b[58]),m=s(m,g,h,f,D,21,b[59]),f=s(f,m,g,h,z,6,b[60]),h=s(h,f,m,g,C,10,b[61]),g=s(g,h,f,m,j,15,b[62]),m=s(m,g,h,f,A,21,b[63]);a[0]=a[0]+f|0;a[1]=a[1]+m|0;a[2]=a[2]+g|0;a[3]=a[3]+h|0},_doFinalize:function(){var b=this._data,n=b.words,a=8*this._nDataBytes,c=8*b.sigBytes;n[c>>>5]|=128<<24-c%32;var e=u.floor(a/
4294967296);n[(c+64>>>9<<4)+15]=(e<<8|e>>>24)&16711935|(e<<24|e>>>8)&4278255360;n[(c+64>>>9<<4)+14]=(a<<8|a>>>24)&16711935|(a<<24|a>>>8)&4278255360;b.sigBytes=4*(n.length+1);this._process();b=this._hash;n=b.words;for(a=0;4>a;a++)c=n[a],n[a]=(c<<8|c>>>24)&16711935|(c<<24|c>>>8)&4278255360;return b},clone:function(){var b=v.clone.call(this);b._hash=this._hash.clone();return b}});t.MD5=v._createHelper(r);t.HmacMD5=v._createHmacHelper(r)})(Math);
(function(){var u=CryptoJS,p=u.lib,d=p.Base,l=p.WordArray,p=u.algo,s=p.EvpKDF=d.extend({cfg:d.extend({keySize:4,hasher:p.MD5,iterations:1}),init:function(d){this.cfg=this.cfg.extend(d)},compute:function(d,r){for(var p=this.cfg,s=p.hasher.create(),b=l.create(),u=b.words,q=p.keySize,p=p.iterations;u.length<q;){n&&s.update(n);var n=s.update(d).finalize(r);s.reset();for(var a=1;a<p;a++)n=s.finalize(n),s.reset();b.concat(n)}b.sigBytes=4*q;return b}});u.EvpKDF=function(d,l,p){return s.create(p).compute(d,
l)}})();
CryptoJS.lib.Cipher||function(u){var p=CryptoJS,d=p.lib,l=d.Base,s=d.WordArray,t=d.BufferedBlockAlgorithm,r=p.enc.Base64,w=p.algo.EvpKDF,v=d.Cipher=t.extend({cfg:l.extend(),createEncryptor:function(e,a){return this.create(this._ENC_XFORM_MODE,e,a)},createDecryptor:function(e,a){return this.create(this._DEC_XFORM_MODE,e,a)},init:function(e,a,b){this.cfg=this.cfg.extend(b);this._xformMode=e;this._key=a;this.reset()},reset:function(){t.reset.call(this);this._doReset()},process:function(e){this._append(e);return this._process()},
finalize:function(e){e&&this._append(e);return this._doFinalize()},keySize:4,ivSize:4,_ENC_XFORM_MODE:1,_DEC_XFORM_MODE:2,_createHelper:function(e){return{encrypt:function(b,k,d){return("string"==typeof k?c:a).encrypt(e,b,k,d)},decrypt:function(b,k,d){return("string"==typeof k?c:a).decrypt(e,b,k,d)}}}});d.StreamCipher=v.extend({_doFinalize:function(){return this._process(!0)},blockSize:1});var b=p.mode={},x=function(e,a,b){var c=this._iv;c?this._iv=u:c=this._prevBlock;for(var d=0;d<b;d++)e[a+d]^=
c[d]},q=(d.BlockCipherMode=l.extend({createEncryptor:function(e,a){return this.Encryptor.create(e,a)},createDecryptor:function(e,a){return this.Decryptor.create(e,a)},init:function(e,a){this._cipher=e;this._iv=a}})).extend();q.Encryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize;x.call(this,e,a,c);b.encryptBlock(e,a);this._prevBlock=e.slice(a,a+c)}});q.Decryptor=q.extend({processBlock:function(e,a){var b=this._cipher,c=b.blockSize,d=e.slice(a,a+c);b.decryptBlock(e,a);x.call(this,
e,a,c);this._prevBlock=d}});b=b.CBC=q;q=(p.pad={}).Pkcs7={pad:function(a,b){for(var c=4*b,c=c-a.sigBytes%c,d=c<<24|c<<16|c<<8|c,l=[],n=0;n<c;n+=4)l.push(d);c=s.create(l,c);a.concat(c)},unpad:function(a){a.sigBytes-=a.words[a.sigBytes-1>>>2]&255}};d.BlockCipher=v.extend({cfg:v.cfg.extend({mode:b,padding:q}),reset:function(){v.reset.call(this);var a=this.cfg,b=a.iv,a=a.mode;if(this._xformMode==this._ENC_XFORM_MODE)var c=a.createEncryptor;else c=a.createDecryptor,this._minBufferSize=1;this._mode=c.call(a,
this,b&&b.words)},_doProcessBlock:function(a,b){this._mode.processBlock(a,b)},_doFinalize:function(){var a=this.cfg.padding;if(this._xformMode==this._ENC_XFORM_MODE){a.pad(this._data,this.blockSize);var b=this._process(!0)}else b=this._process(!0),a.unpad(b);return b},blockSize:4});var n=d.CipherParams=l.extend({init:function(a){this.mixIn(a)},toString:function(a){return(a||this.formatter).stringify(this)}}),b=(p.format={}).OpenSSL={stringify:function(a){var b=a.ciphertext;a=a.salt;return(a?s.create([1398893684,
1701076831]).concat(a).concat(b):b).toString(r)},parse:function(a){a=r.parse(a);var b=a.words;if(1398893684==b[0]&&1701076831==b[1]){var c=s.create(b.slice(2,4));b.splice(0,4);a.sigBytes-=16}return n.create({ciphertext:a,salt:c})}},a=d.SerializableCipher=l.extend({cfg:l.extend({format:b}),encrypt:function(a,b,c,d){d=this.cfg.extend(d);var l=a.createEncryptor(c,d);b=l.finalize(b);l=l.cfg;return n.create({ciphertext:b,key:c,iv:l.iv,algorithm:a,mode:l.mode,padding:l.padding,blockSize:a.blockSize,formatter:d.format})},
decrypt:function(a,b,c,d){d=this.cfg.extend(d);b=this._parse(b,d.format);return a.createDecryptor(c,d).finalize(b.ciphertext)},_parse:function(a,b){return"string"==typeof a?b.parse(a,this):a}}),p=(p.kdf={}).OpenSSL={execute:function(a,b,c,d){d||(d=s.random(8));a=w.create({keySize:b+c}).compute(a,d);c=s.create(a.words.slice(b),4*c);a.sigBytes=4*b;return n.create({key:a,iv:c,salt:d})}},c=d.PasswordBasedCipher=a.extend({cfg:a.cfg.extend({kdf:p}),encrypt:function(b,c,d,l){l=this.cfg.extend(l);d=l.kdf.execute(d,
b.keySize,b.ivSize);l.iv=d.iv;b=a.encrypt.call(this,b,c,d.key,l);b.mixIn(d);return b},decrypt:function(b,c,d,l){l=this.cfg.extend(l);c=this._parse(c,l.format);d=l.kdf.execute(d,b.keySize,b.ivSize,c.salt);l.iv=d.iv;return a.decrypt.call(this,b,c,d.key,l)}})}();
(function(){for(var u=CryptoJS,p=u.lib.BlockCipher,d=u.algo,l=[],s=[],t=[],r=[],w=[],v=[],b=[],x=[],q=[],n=[],a=[],c=0;256>c;c++)a[c]=128>c?c<<1:c<<1^283;for(var e=0,j=0,c=0;256>c;c++){var k=j^j<<1^j<<2^j<<3^j<<4,k=k>>>8^k&255^99;l[e]=k;s[k]=e;var z=a[e],F=a[z],G=a[F],y=257*a[k]^16843008*k;t[e]=y<<24|y>>>8;r[e]=y<<16|y>>>16;w[e]=y<<8|y>>>24;v[e]=y;y=16843009*G^65537*F^257*z^16843008*e;b[k]=y<<24|y>>>8;x[k]=y<<16|y>>>16;q[k]=y<<8|y>>>24;n[k]=y;e?(e=z^a[a[a[G^z]]],j^=a[a[j]]):e=j=1}var H=[0,1,2,4,8,
16,32,64,128,27,54],d=d.AES=p.extend({_doReset:function(){for(var a=this._key,c=a.words,d=a.sigBytes/4,a=4*((this._nRounds=d+6)+1),e=this._keySchedule=[],j=0;j<a;j++)if(j<d)e[j]=c[j];else{var k=e[j-1];j%d?6<d&&4==j%d&&(k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255]):(k=k<<8|k>>>24,k=l[k>>>24]<<24|l[k>>>16&255]<<16|l[k>>>8&255]<<8|l[k&255],k^=H[j/d|0]<<24);e[j]=e[j-d]^k}c=this._invKeySchedule=[];for(d=0;d<a;d++)j=a-d,k=d%4?e[j]:e[j-4],c[d]=4>d||4>=j?k:b[l[k>>>24]]^x[l[k>>>16&255]]^q[l[k>>>
8&255]]^n[l[k&255]]},encryptBlock:function(a,b){this._doCryptBlock(a,b,this._keySchedule,t,r,w,v,l)},decryptBlock:function(a,c){var d=a[c+1];a[c+1]=a[c+3];a[c+3]=d;this._doCryptBlock(a,c,this._invKeySchedule,b,x,q,n,s);d=a[c+1];a[c+1]=a[c+3];a[c+3]=d},_doCryptBlock:function(a,b,c,d,e,j,l,f){for(var m=this._nRounds,g=a[b]^c[0],h=a[b+1]^c[1],k=a[b+2]^c[2],n=a[b+3]^c[3],p=4,r=1;r<m;r++)var q=d[g>>>24]^e[h>>>16&255]^j[k>>>8&255]^l[n&255]^c[p++],s=d[h>>>24]^e[k>>>16&255]^j[n>>>8&255]^l[g&255]^c[p++],t=
d[k>>>24]^e[n>>>16&255]^j[g>>>8&255]^l[h&255]^c[p++],n=d[n>>>24]^e[g>>>16&255]^j[h>>>8&255]^l[k&255]^c[p++],g=q,h=s,k=t;q=(f[g>>>24]<<24|f[h>>>16&255]<<16|f[k>>>8&255]<<8|f[n&255])^c[p++];s=(f[h>>>24]<<24|f[k>>>16&255]<<16|f[n>>>8&255]<<8|f[g&255])^c[p++];t=(f[k>>>24]<<24|f[n>>>16&255]<<16|f[g>>>8&255]<<8|f[h&255])^c[p++];n=(f[n>>>24]<<24|f[g>>>16&255]<<16|f[h>>>8&255]<<8|f[k&255])^c[p++];a[b]=q;a[b+1]=s;a[b+2]=t;a[b+3]=n},keySize:8});u.AES=p._createHelper(d)})();
CryptoJS.mode.ECB = (function () {
  var ECB = CryptoJS.lib.BlockCipherMode.extend();

  ECB.Encryptor = ECB.extend({
    processBlock: function (words, offset) {
      this._cipher.encryptBlock(words, offset);
    }
  });

  ECB.Decryptor = ECB.extend({
    processBlock: function (words, offset) {
      this._cipher.decryptBlock(words, offset);
    }
  });

  return ECB;
}());
module.exports = {
  CryptoJS: CryptoJS
}

第二部分,即配置文件

任意命名在此暂且叫它aesUtil.js吧,代码如下:

// util 文件
var aes = require('aes.js'); //引入aes类包
//URl: https://github.com/kk196110/wechat/blob/master/utils/aes.js
 
//十六位十六进制数作为秘钥
var aeskey = aes.CryptoJS.enc.Utf8.parse("pxcvbnmaskoghjkl");
//十六位十六进制数作为秘钥偏移量
var aesiv = aes.CryptoJS.enc.Utf8.parse('frbvcxzlkhugfdhy');
 
// 加密
function encrypt(data) {
    var srcs = aes.CryptoJS.enc.Utf8.parse(data);
    var encrypted = aes.CryptoJS.AES.encrypt(srcs, aeskey, { iv: aesiv, mode: aes.CryptoJS.mode.CBC, padding: aes.CryptoJS.pad.Pkcs7 });
    //返回base64加密结果
    return encrypted.toString();
}
 
//解密
function decrypt(data) {
   // data是base64编码数据
    var decrypt = aes.CryptoJS.AES.decrypt(data, aeskey, { iv: aesiv, mode: aes.CryptoJS.mode.CBC, padding: aes.CryptoJS.pad.Pkcs7 });
    var decryptedStr = decrypt.toString(aes.CryptoJS.enc.Utf8);
    return decryptedStr.toString();
}
 
module.exports ={
     encrypt: encrypt,
     decrypt: decrypt
}

第三部分:引用

微信小程序端引用,比如首页index.js用它。

const encrypt = require('../../utils/aesUtil');
console.log(encrypt.AESEncrypt('hello md5 你好 _-% % &123'))
console.log(encrypt.AESDecrypt('1OloInqltdND24HK7GuecEtkn06kX0h4jPDVRHMGpe8='))
//结果:
//1OloInqltdND24HK7GuecEtkn06kX0h4jPDVRHMGpe8=
//hello md5 你好 _-% % &123









一、导语【坑】:

项目中遇到删除列表中某一个item的需求,当使用splice的时候,理解有问题。当使用下面语句:

let item = this.data.items.splice(0, 1)

时,实际上它它返回的item是是被删除的那一条,不是返回的删除后剩下的数组。NND!

.

QQ20221006-135627@2x.png

二、演示一下正确的用法:

js代码:

Page({
    data: {
        godness: [
            {name: "张三"}, 
            {name: "李四"}, 
            {name: "王五"}, 
            {name: "赵六"}
        ]
    },
    onLoad: function(options) {},
    deleteBtn: function(event) {
        let index = event.currentTarget.dataset.index
        this.data.godness.splice(index, 1)
        this.setData({
            godness: this.data.godness //这里很重要
        })
    }
})





第一步:新建某个文件formatDate.wxs格式,内容如下:

//这里定义需用var
var formatNumber = function(n){
  if(n < 10) {
    return '0'+ n;
  }
  return n;
};

var time = {
  TimeToDate: function (date, format) {
    format = format || 'YYYY-MM-DD hh:mm:ss';
    var dateTest = (getRegExp('^(-)?\d{1,10}$').test(date) || getRegExp('^(-)?\d{1,13}$').test(date));
    if (getRegExp('^[1-9]*[1-9][0-9]*$').test(date) && dateTest) {
      var vdate = parseInt(date);
      if (getRegExp('^(-)?\d{1,10}$').test(vdate)) {
        vdate = vdate * 1000;
      } else if (getRegExp('^(-)?\d{1,13}$').test(vdate)) {
        //vdate = vdate * 1000;
        vdate = vdate;
      } else if (getRegExp('^(-)?\d{1,14}$').test(vdate)) {
        vdate = vdate * 100;
      } else {
        alert("时间戳格式不正确");
        return;
      }
      var setdate = getDate(vdate);
      return parse({ YYYY: setdate.getFullYear(), MM: digit(setdate.getMonth() + 1), DD: digit(setdate.getDate()), hh: digit(setdate.getHours()), mm: digit(setdate.getMinutes()), ss: digit(setdate.getSeconds()) }, format);
    } else {
      //将日期转换成时间戳
      re = getRegExp('(\d{4})(?:\D?(\d{1,2})(?:\D?(\d{1,2}))?[^\d\s]?)?(?:\s+(\d{1,2})\D?(\d{1,2})\D?(\d{1,2}))?').exec(date);
      return !re ? 0 : getDate(re[1], (re[2] || 1) - 1, re[3] || 1, re[4] || 0, re[5] || 0, re[6] || 0).getTime() / 1000;
    }

    function parse(ymdhms, format) {
      var regymdzz = "YYYY|MM|DD|hh|mm|ss|zz";
      return format.replace(getRegExp(regymdzz, "g"), function (str, index) {
        return str == "zz" ? "00" : digit(ymdhms[str]);
      })
    }
    function digit(num) {
      return num < 10 ? "0" + (num | 0) : num;
    }
  }
}

//小于10的补零操作
function add(m){
  return m < 10 ? '0' + m : m 
}


module.exports = {
  formatNumber: formatNumber,
  TimeToDate: time.TimeToDate,
}

第二步:

<wxs module="common" src="../../utils/formatDate.wxs"></wxs>
<!--index.wxml-->
<view>
    <!-- 时间戳转日期 -->
    <view>1575429950</view>
    <view>{{common.TimeToDate(1575429950)}}</view>
    <view>{{common.TimeToDate(1575429950,'YYYY年MM月DD日 hh时mm分ss秒')}}</view>
    <view>   </view>
    <!-- 日期转时间戳 -->
    <view>2019-12-04 11:25:50</view>
    <view>{{common.TimeToDate('2019-12-04 11:25:50')}}</view>
    <view>{{common.TimeToDate('2019年12月04日 11时25分50秒')}}</view>
</view>

第三步:效果如下
QQ20221005-233831@2x.png

转自:https://fengkui.net/articles/110

PHP,JAVA,NET 开发比较

一、语言


PHP:PHP产生与1994年,其语法混合了C、Java、Perl 和他自创的一些编程语法;PHP是嵌入在HTML中执行的;它也是一种解释性语言。早期的PHP并非完全的面向对象编程语言,到了PHP4以后的版本才开始有了面向对象的概念。

JAVA:JAVA产生与1995年,JAVA语言和JAVA平台统称为JAVA;它语法与C语言和C++语言很接近,并且JAVA是面向对象编程语言,JAVA是编译性语言,可以先将JAVA源码编译成.class文件后,在JAVA虚拟机上解释执行。

.NET:在.NET中,多种编程语言支持开发如:VB、C#、F#等等,通常我们都在使用C#编程,C#是为.NET平台专们打造的一种编程语言,产生与2000年。其语言语法和JAVA、C、C++相近,同样也是一种面向对象编程语言。C#同样需要编译为.dll文件,然后由.net框架中CLR编译执行。

语言区分:PHP、JAVA、C#这几天语言最大的区别就是执行方式的不同。其中还有一些语言本身的区别,比如是否为强类型、是否能动态编译、是否为多线程、是否为分布式、是否可移值等等。至于很多人说的性能上的问题,我认为语言本身上的性能问题差异并不大,关建在于写程序的人如何编写的代码。另外,在关于与语言结和的数据库方面也有一定的区别,PHP通常使用MySQL数据库,JAVA通常使用MySQL或Oracle,而C#因为是微软的产物,通常使用与微软相关的数据库Sql Server或Access等,有时候也会用Sqllite数据库。

二、平台:

PHP:一般人在称呼PHP的时候,本身并没有平台和语言的区别。我们用PHP往往只做WEB应用开发,至于桌面应用程序的开发,近年好像PHP出了这种开发平台,但现实应用中几乎看不到。但是不得不说,PHP在WEB的表现层应用中,有很好的表现,不论从处理界面布局,或是性能上都有着不错的优势。

JAVA:我们在称呼JAVA的时候,往往说的并不一定是语言本身,而是指JAVA平台。在JAVA平台中,可以使用JAVA语言去开发各种不同的应用开发,比如说:Java SE、Java EE和Java ME,分别用于开发JAVA桌面应用、WEB应用、移动应用等等。

.NET:在.NET这个体系中,语言和平台是有明显区别的,而且一个平台上可以应用多种语言开发,这样就满足了掌握不同语言的程序员可以开发同一个应用程序。在.NET平台中,也像JAVA一样,可以开发不同的应用,比如:WinForm(桌面应用)、控制台应用、ASP.NET(WEB应用)、WPF(新的桌面应用)、WCF(网络通信基础应用)、WEB服务(面向服务编程应用)、ASP.NET MVC3.0(新的WEB应用)、XNA(桌面及手机游戏应用)等等。

平台区分:三种技术平台都可以做我们常用的WEB应用。对于桌面应用来说,PHP并不太适用,JAVA则没有较好的桌面应用的开发工具,这方面.NET平台有较好的优势,不论是Winform还是WPF,都非常适合做桌面应用程序。至于实现一些底层的复杂业务,PHP则不如JAVA和.NET,但是在做前端表现层的时候有着较好的优势。所以很多复杂的大型综合应用,可能会有.NET或者JAVA做数据访问层及业务逻辑层,PHP则用来做表现层。据所说淘宝就是基于这种方式开发的。同时,JAVA与.NET(mono)都是可以跨平台的,.NET还能跨语言。

二。学.Net还是学Java

因为.Net和Java是国内市场占有率最高的两门技术,对于准备学习编程语言的初学者来说,.Net和Java是初学者首先考虑的两门技术,因此很多人一遍遍的问“学.Net还是学Java”,社区中也每天都有“.Net控”在唱衰Java,也有“Java控”在唱衰.Net,一时间硝烟四起,让初学者更加迷惑。

     从北京、广州、上海等地区2010年的就业统计数据(由于没有找到更权威的数据,这些数据是我对主流招聘网站和技术网站的相关数据进行的并不是太严谨的分析)来看,.Net和Java的职位数量比例为2:3,从就业网站和主要技术社区数据来看,2010年.Net和Java的学习人员数量比例为1:3。从数据上看,.Net的职位绝对数量比Java低一些,不过Java学习人员数量比.Net学习人员高出3倍,因此.Net职位的竞争激烈程度低于Java。

 

    由于.Net秉承了微软技术的入门简单的特点,经过短时间的学习就可以掌握拖拉控件的快速开发方式,很多人被.Net的快速开发方式所满足,认为自己“学成了”,不再深入研究,因此人才市场上充斥着很多只会拖控件、而不懂.Net更深入一步技术的.Net开发人员,比如很多ASP.Net的书上都是讲ASP.Net服务端控件,很多所谓“精通ASP.Net”的人连什么是Http、HTML、JavaScript、Dom等基本概念都稀里糊涂,实际公司项目中很多功能点的实现不是简单的拖一个ASP.Net服务端控件就能搞定的,因此看似学习.Net的人非常多,但是真正满足企业要求的人则比较少;而Java则入门门槛比较高,如果想要配置一个Hello World级别的学习环境就会涉及到很多技术,开发环境也要学习者自行配置,对于初学者来说可能花费一周时间都无法配置一个最简单的环境出来,Java的开发环境也不像.Net开发环境那么可用性强,Java和.Net的区别就像Windows和Linux的区别,正因为Java学习门槛非常高,很多学习者被淘汰了下来,这样能够撑过一个个难关学习下来的开发人员都是水平比较高的人员。因此.Net初级开发人员的工资平均是比Java初级开发人员的工资平均略低的。但是由于.Net中高级人才非常缺乏,因此掌握.Net深入技术的开发人员在企业内部是非常吃香的,招聘人员经常发出这样的感叹“招合适的Java工程师能招来一堆,招合适的.Net工程师一个月都招不来几个”。

 

    .Net入门快,在.Net中微软为开发人员提供了一套最佳的技术架构搭配、集成的开发环境,用微软的技术架构开发出的系统就可以保证最好的效果,而不用像Java开发人员那样去学很多开源框架再去学习搭建技术架构、开发环境,因此.Net学习周期比Java短,有了语言基础后再进行集训式的就业班学习,那么.Net学习者学习3个月可以进入就业,Java学习者需要5个月可以进入就业。

 

    微软的本地化推广工作做得非常好,因此学习.Net有大量的中文资料可以参考,因此除非研究很高深的领域,否则中文资料基本能满足开发者的需求;而Java由于走的是高端路线,因此除了初学者资料外,大部分Java资料都是英文的,学习Java需要参考很多英文资料。因此如果英文非常好的学员学习Java就会很轻松,而英文不是很好的学员学习.Net会更好。

 

    综上,Java入门门槛比较高,需要有足够的毅力和百折不挠的精神,只要你能坚持下来你就可以将很多人在入门门槛上就将很多竞争者甩在身后。.Net入门门槛比较低,装上VisualStudio就可以马上进行开发,不需要进行太多的配置、不需要学太多基础知识,这样可以很轻松的掌握入门.Net学习,是有很成就感的事情,能让你有兴趣进一步学习,但是你不能止步于此,因此你会发现身边也有无数的人也是“轻松入门”,你就需要继续深入学习,深入学习同样需要毅力和探索的精神,你要在这个阶段上把绝大部分竞争者甩在身后,小吹一把:我教的有个学生面试回来说“在咱班里比我感觉我掌握的不是太好,但是和一起参加面试的北大某鸟的学生比我简直就是高手”,呵呵。在我讲“.Net入门简单”的时候,有学生问“.Net把问题都简化了岂不是.Net程序员都不值钱了?”,我的回答是“微软简化的是重复性的东西和低级的东西,这样我们就能从这些低级的工作中脱身出来研究更深入的东西,因此.Net把问题简化了只是使得那些只沉迷于简单的东西的人不值钱了,会使得研究深入的人更值钱”,这就是“.Net入门容易,深入同样难”这句话的意思。有人会问“哪些知识属于深入的知识呢?”,比如你不仅要会用ADO.Net,还要研究ADO.Net架构中的设计模式,再比如你不仅要会拖拉ASP.Net控件,更要明白ASP.Net控件内部发生了什么。,如果你想从学习一开始就尽快脱离控件型开发人员的行列,你可以每天登陆博客园,csdn等高端.Net社区,你会发现你进步是非常快的。

 

    可能对.Net的描述中有或多或少的偏向,但是抛去个人因素考虑,我没有暗示Java好还是.Net好,请根据自己的情况进行选择。而且.Net和Java并不像360和QQ那样必须二者选其一,并不像很多初学者认为的那样“学一门语言就靠这门语言吃一辈子”,工作中很可能这个项目用Java,那个项目用.Net,因此你可以喜欢某个语言,但是不要抱着一门语言不放,更不用担心“Sun被Oracle收购了,Java是不是完了?”、“我学.Net,如果微软没落了我是不是就失业了”之类的问题,不同语言只是不同的工具,换了一个新的工具可以很快的熟悉新的工具,大部分内容都是相通的,比如java中的web开发很熟悉了,转而用ASP.Net也会发现大部分东西都是和JavaWeb一样的,很快就能上手ASP.Net,难道你现在用的是诺基亚手机,你会害怕诺基亚倒台你就不会用手机了吗?“Java完了、微软死了”这类问题不是开发人员去关心的事情,而是华尔街那些金融大鳄去考虑的问题,咱们只管用技术去赚钱养家就是了。

 

Java、.Net、PHP 市场应用

我们把Java .Net PHP应用方面占有率做个比较,简单的把目前主流应用分成两个大类,一个是企业应用,一个是Web网站应用,下面这个表格是我归纳的,不一定准确,但是能说明一个大概。

应用 / 语言 Java .Net PHP
大型企业应用
中型企业应用
小型企业应用
大型Web应用
中型Web应用
小型Web应用
从表中可以看到,Java和PHP都有各自擅长的领域,但是.Net却没有突出的地方,从占有率来看情况十分尴尬。
我们再来看看技术方面,首先声明,我对其中每种语言技术都不是很熟悉,只能大概分析一下...
先说说Java,在企业级方面,可以说是绝对的老大,许多企业级技术,开发思想都是由Java发展出来的。缺点是Java开发部署比较麻烦 ,不太适合超小型的项目。
再说.Net,在1.x时代,.Net可以说基本上没有多少企业级开发的特性,到了3.0,微软各种框架技术虽然弥补了这些不足,但是相对于Java世界,还是有一定距离。 在Web网站方面,.Net服务器控件的优势,变成了弱势,由于服务器空间产生垃圾代码,并且不方便美工调整,导致在前台界面要求较高的门户站点难以使用(虽然有第三方MVC框架,但是没有IDE支持,体现不出.Net的优势)
再说说PHP,他的定位非常明显,就是Web开发,所以有很多适合Web开发的特性,比如部署十分简单,几个文件随便找个虚拟主机扔上去就能运行。在国内因为Discuz , DedeCMS等著名产品的鼎立推广,PHP在中小型网站开发中有很大的优势.,最近大量的开源框架出现,给PHP企业开发注入了一些生命力,可以说潜力十足。
综合以上我们可以看到,.Net定位不太明确,微软这个想吃那个也想吃,最后没一个能吃饱吃好..
 
 
 
四。创业项目,你选择什么样的开发语言
1、需要尽量节省成本;2、对效率需求很大;3、有扩展性需求;4、需要考虑可移植性;
只选择主流语言:.Net、Java、PHP这类的语言。
.Net 属于微软的“个性”产品,可移植性不够,否掉。
Java语言,太“高端”,开发人员成本相对较高,而开发效率稍差一些。
PHP语言,出现时间比较长,语言相对比较成熟。开发人员比JAVA成本略低一些,开发效率比JAVA快。
PHP与平台无关可移植性比较好,代码几乎可以不用修改的在WINDOWS、LINUX上通用。
PHP的性能比JAVA略差,但是从这个项目对性能的需求来看,在可预期的时间内,还足够应付。

总结:请注意,不论任何编程语言或技术平台,并没有好与不好之分,只有适合与否。在开发不同的项目时需要使用不同的技术。当然做为程序员初学者来说,需要从一种比较容易上手的编程语言及平台学起,培养自己的学习能力、兴趣;基础打扎实,才能在以后日新月异的技术更新中不断成长。

最后代表个人一点小观点:现在开始学it的话,我建议学php(web)或者java(手机)

当然.net开发者就要看微软了,我希望是王者归来雄霸天下,跟着微软走永远有混头!

.net开发的优势在于:快速开发(服务器控件做后台非常好),后期维护成本低,当然在中国它还有一个优势就是开发成本低,你懂得!

.net web开发的模式:webform (中小项目),aspnet mvc(大中项目)

转自:https://blog.csdn.net/liuhhaiffeng/article/details/75453699

全角:是一种电脑字符,是指一个全角字符占用两个标准字符(或两个半角字符)的位置。全角占两个字节。 半角:是指一个字符占用一个标准的字符位置。半角占一个字节。 不管是半角还是全角,汉字都要占两个字节。
&#12288;  == 一个中文宽度

&#32; == 普通的英文半角空格

&#160; == &nbsp; == &#xA0; == no-break space (普通的英文半角空格但不换行)

&#8194; == &ensp; == en空格 (半个中文宽度)

&#8195; == &emsp; == em空格 (一个中文宽度)

&#8197; == 四分之一em空格 (四分之一中文宽度)

相比平时的空格(&#32;),&nbsp拥有不间断(non-breaking)特性。即连续的&nbsp会在同一行内显示。
即使有100个连续的nbsp,浏览器也不会把它们拆成两行。

链接:https://juejin.cn/post/6844904016971825165