id: 592e842cad444bc4bb498ae13f372498
parent_id: 
item_type: 1
item_id: 13ac1745af964e6b8d239fc434a7a919
item_updated_time: 1764345477870
title_diff: "[{\"diffs\":[[1,\"不需内测账号，带你体验微信小程序完整开发过程\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":22}]"
body_diff: "[{\"diffs\":[[1,\"<html>\\\r\\\n<head>\\\r\\\n  <title>不需内测账号，带你体验微信小程序完整开发过程</title>\\\r\\\n  <basefont face=\\\"微软雅黑\\\" size=\\\"2\\\" />\\\r\\\n  <meta http-equiv=\\\"Content-Type\\\" content=\\\"text/html;charset=utf-8\\\" />\\\r\\\n  <meta name=\\\"exporter-version\\\" content=\\\"YXBJ Windows/608924 (zh-CN, DDL); Windows/10.0.0 (Win64); EDAMVersion=V2;\\\"/>\\\r\\\n  <meta name=\\\"application-data:2525275\\\" content=\\\"6692893/s30/a6bc9e18-4408-42a0-89b8-b08cc6a7aa3e/app.yinxiang.com\\\"/>\\\r\\\n  <style>\\\r\\\n\\tbody, td {\\\r\\\n\\t  font-family: 微软雅黑;\\\r\\\n\\t  font-size: 12pt;\\\r\\\n\\t}\\\r\\\n  </style>\\\r\\\n</head>\\\r\\\n<body>\\\r\\\n<a name=\\\"10390\\\"></a>\\\r\\\n<h1>不需内测账号，带你体验微信小程序完整开发过程</h1>\\\r\\\n\\\r\\\n<div><span><div style=\\\"-evernote-webclip:true\\\"><br/><div><div><div><div><h1> 不需内测账号，带你体验微信小程序完整开发过程 </h1><div><div></div></div></div>\\\n  \\\n\\\n\\t\\t\\\n  <span>2016年09月24日</span>\\\n - 作者: \\\n\\t\\\n\\t\\\n\\t\\\n\\t\\\n  \\\n\\t\\\n\\t\\\n    \\\n    \\t<p>微信小程序还没正式发布就已经迅速成为大家讨论的焦点，那么大家可能觉得只有收到内测邀请才能体验小程序的开发流程，其实不然，大家都可以体验，下面就带大家一起了解。</p>\\\n<h2>下载微信 Web 开发者工具</h2><p>首先，微信给我们提供了它自己的小程序集成开发工具，只需要到这个页面下载即可：</p>\\\n<p><a href=\\\"https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=1474644089359\\\" target=\\\"_blank\\\">https://mp.weixin.qq.com/debug/wxadoc/dev/devtools/download.html?t=1474644089359</a></p>\\\n<p>下载完成后，打开开发者工具，会有一个扫码登录界面。 用你的微信扫码就可以登录进来了， 然后开发者工具会帮我们创建一个默认工程，项目的文件结构如下：</p>\\\n<p><a href=\\\"http://swiftcafe.io/articleimg/wx-app/1.png\\\" target=\\\"_blank\\\"></a></p><div><a href=\\\"http://swiftcafe.io/articleimg/wx-app/1.png\\\" target=\\\"_blank\\\"><img src=\\\":/4e0205a1e6d1447ea63bf96876602687\\\" type=\\\"image/png\\\" data-filename=\\\"Image.png\\\" height=\\\"402\\\" width=\\\"576\\\"/></a></div><p></p>\\\n<p>所有的代码编辑以及运行预览都可以在这个开发者工具中进行。接下来咱们看看微信小程序的项目结构。</p>\\\n<h2>项目结构</h2><p>如上图，首先在根目录中有三个文件 app.js, app.json, app.wxss。 其中 app.js 是程序主入口的脚本文件， app.json 是全局配置文件, app.wxss 是小程序的样式表文件。</p>\\\n<p>先来看看 app.json :</p>\\\n<div><table><tbody><tr><td><pre><div>{</div><div>  &quot;pages&quot;:[</div><div>    &quot;pages/index/index&quot;,</div><div>    &quot;pages/logs/logs&quot;</div><div>  ],</div><div>  &quot;window&quot;:{</div><div>    &quot;backgroundTextStyle&quot;:&quot;light&quot;,</div><div>    &quot;navigationBarBackgroundColor&quot;: &quot;#fff&quot;,</div><div>    &quot;navigationBarTitleText&quot;: &quot;WeChat&quot;,</div><div>    &quot;navigationBarTextStyle&quot;:&quot;black&quot;</div><div>  }</div><div>}</div></pre></td></tr></tbody></table></div>\\\n<p>这个配置文件中定义了两个节点， pages 是小程序的所有页面对应的路径， window 是小程序窗口的配置信息。</p>\\\n<p>再来看看样式文件 app.wxss :</p>\\\n<div><table><tbody><tr><td><pre><div>.container {</div><div>  height: 100%;</div><div>  display: flex;</div><div>  flex-direction: column;</div><div>  align-items: center;</div><div>  justify-content: space-between;</div><div>  padding: 200rpx 0;</div><div>  box-sizing: border-box;</div><div>}</div></pre></td></tr></tbody></table></div>\\\n<p>咱们先不用深究它具体定义了什么样式，只需要先了解项目结构即可。接下来再来看看程序的主入口 app.js：</p>\\\n<div><table><tbody><tr><td><pre><div>//app.js</div><div>App({</div><div>  onLaunch: function () {</div><div>    //调用API从本地缓存中获取数据</div><div>    var logs = wx.getStorageSync('logs') || []</div><div>    logs.unshift(Date.now())</div><div>    wx.setStorageSync('logs', logs)</div><div>  },</div><div>  getUserInfo:function(cb){</div><div>    var that = this</div><div>    if(this.globalData.userInfo){</div><div>      typeof cb == &quot;function&quot; &amp;&amp; cb(this.globalData.userInfo)</div><div>    }else{</div><div>      //调用登录接口</div><div>      wx.login({</div><div>        success: function () {</div><div>          wx.getUserInfo({</div><div>            success: function (res) {</div><div>              that.globalData.userInfo = res.userInfo</div><div>              typeof cb == &quot;function&quot; &amp;&amp; cb(that.globalData.userInfo)</div><div>            }</div><div>          })</div><div>        }</div><div>      })</div><div>    }</div><div>  },</div><div>  globalData:{</div><div>    userInfo:null</div><div>  }</div><div>})</div></pre></td></tr></tbody></table></div>\\\n<p>这里初始化了一个 App 对象，并且定义了三个方法 <code>onLaunch</code>，<code>getUserInfo</code> 和 <code>globalData</code>。  先来看看 <code>onLaunch</code>:</p>\\\n<div><table><tbody><tr><td><pre><div>onLaunch: function () {</div><div>\\t//调用API从本地缓存中获取数据</div><div>\\tvar logs = wx.getStorageSync('logs') || []</div><div>\\tlogs.unshift(Date.now())</div><div>\\twx.setStorageSync('logs', logs)</div><div>}</div></pre></td></tr></tbody></table></div>\\\n<p>首先 <code>wx.getStorageSync</code> 方法会获得本地的一个以 logs 为 key 的缓存数据。 传入这个方法的 <code>logs</code> 本身没有任何特殊含义，只是用于表示我们使用的缓存数据。 这个机制可以理解为和 iOS 的 NSUserDefaults 类似。</p>\\\n<p>然后，我们想这个缓存数组中插入当前的日期 <code>logs.unshift(Date.now())</code>。 最后再调用 setStorageSync 方法将我们新的缓存内容写入到本地缓存中。</p>\\\n<p>因为 onLaunch 方法是小程序的生命周期方法，所以在小程序启动的时候就会调用它，并将当前启动的日期记录并写入本地缓存中。 没错 onLaunch 整个方法就干了这件事儿。</p>\\\n<p>我们再来看下 getUserInfo 方法，它通过调用 wx.login 和 wx.getUserInfo 两个微信平台的函数获取当前用户登录信息，并传会给回调函数 cb：</p>\\\n<div><table><tbody><tr><td><pre><div>getUserInfo:function(cb){</div><div>    var that = this</div><div>    if(this.globalData.userInfo){</div><div>      typeof cb == &quot;function&quot; &amp;&amp; cb(this.globalData.userInfo)</div><div>    }else{</div><div>      //调用登录接口</div><div>      wx.login({</div><div>        success: function () {</div><div>          wx.getUserInfo({</div><div>            success: function (res) {</div><div>              that.globalData.userInfo = res.userInfo</div><div>              typeof cb == &quot;function&quot; &amp;&amp; cb(that.globalData.userInfo)</div><div>            }</div><div>          })</div><div>        }</div><div>      })</div><div>    }</div><div>}</div></pre></td></tr></tbody></table></div>\\\n<p>至于最开始的 if 判断 <code>if(this.globalData.userInfo)</code> 咱们暂时不用深究，只看 else 部分即可。</p>\\\n<h2>页面结构</h2><p>了解完根目录的几个文件， 咱们再来看看页面文件, 正如咱们刚开始截图中看到的项目结构：</p>\\\n<p><a href=\\\"http://swiftcafe.io/articleimg/wx-app/2.png\\\" target=\\\"_blank\\\"></a></p><div><a href=\\\"http://swiftcafe.io/articleimg/wx-app/2.png\\\" target=\\\"_blank\\\"><img src=\\\":/cde56301da864ef6a06344eeabeb68f7\\\" type=\\\"image/png\\\" data-filename=\\\"Image.png\\\" height=\\\"294\\\" width=\\\"301\\\"/></a></div><p></p>\\\n<p>所有的页面都在 pages 文件夹中。 我们这个示例工程中有两个页面 index 和 logs。 还记得我们前面在 app.json 看到的页面配置吗：</p>\\\n<div><table><tbody><tr><td><pre><div>&quot;pages&quot;:[</div><div>    &quot;pages/index/index&quot;,</div><div>    &quot;pages/logs/logs&quot;</div><div>]</div></pre></td></tr></tbody></table></div>\\\n<p>正好对应上咱们现在看到的两个目录， 还要记得一点， pages 数组中的第一个元素会作为我们小程序的主页。 切记，index 页面之所以是首页，是因为它是 pages 里面的第一个元素， 而不是因为它的名称是 index。</p>\\\n<p>我们来看看 index 页面的构成， index.js, index.wxml, index.wxss。 index.js 是页面的脚本文件, index.wxml 是页面的 UI 文件， index.wxss 是页面的样式文件。 </p>\\\n<p>先看一下 index.js:</p>\\\n<div><table><tbody><tr><td><pre><div>//index.js</div><div>//获取应用实例</div><div>var app = getApp()</div><div>Page({</div><div>  data: {</div><div>    motto: 'Hello World',</div><div>    userInfo: {}</div><div>  },</div><div>  //事件处理函数</div><div>  bindViewTap: function() {</div><div>    wx.navigateTo({</div><div>      url: '../logs/logs'</div><div>    })</div><div>  },</div><div>  onLoad: function () {</div><div>    console.log('onLoad')</div><div>    var that = this</div><div>    //调用应用实例的方法获取全局数据</div><div>    app.getUserInfo(function(userInfo){</div><div>      //更新数据</div><div>      that.setData({</div><div>        userInfo:userInfo</div><div>      })</div><div>    })</div><div>  }</div><div>})</div></pre></td></tr></tbody></table></div>\\\n<p><code>getApp()</code> 方法获取我们的 app 实例。 然后在看 onLoad 方法， 使用我们刚才提到的 getUserInfo 方法获取用户信息，并设置到 data 属性中。 </p>\\\n<p><code>bindViewTap</code> 方法会绑定一个事件，这个事件调用 wx.navigateTo 方法。 这个方法其实就是页面跳转，从代码中也不难看出，跳转到了 logs 页面。</p>\\\n<p>脚本文件就这些内容了，咱们继续再来看看 UI 文件， index.wxml:</p>\\\n<div><table><tbody><tr><td><pre><div>&lt;!-- </div><div>&lt;view class=&quot;container&quot;&gt;</div><div>  &lt;view  bindtap=&quot;bindViewTap&quot; class=&quot;userinfo&quot;&gt;</div><div>    &lt;image class=&quot;userinfo-avatar&quot; src=&quot;{{userInfo.avatarUrl}}&quot; background-size=&quot;cover&quot;&gt;&lt;/image&gt;</div><div>    &lt;text class=&quot;userinfo-nickname&quot;&gt;{{userInfo.nickName}}&lt;/text&gt;</div><div>  &lt;/view&gt;</div><div>  &lt;view class=&quot;usermotto&quot;&gt;</div><div>    &lt;text class=&quot;user-motto&quot;&gt;{{motto}}&lt;/text&gt;</div><div>  &lt;/view&gt;</div><div>&lt;/view&gt;  </div><div>--&gt;</div></pre></td></tr></tbody></table></div>\\\n<p>这个就是小程序 index 页面的 UI 文件了，其实就是微信平台定义了一系列组件，最外层是 <code>&lt;view class=&quot;container&quot;&gt;</code> 还记得 container 么？ 我们在最外层的 app.wxss 定义了它的样式。 它里面包含了两个 View。先来看看第一个：</p>\\\n<div><table><tbody><tr><td><pre><div>&lt;!-- </div><div>&lt;view  bindtap=&quot;bindViewTap&quot; class=&quot;userinfo&quot;&gt;</div><div>  &lt;image class=&quot;userinfo-avatar&quot; src=&quot;{{userInfo.avatarUrl}}&quot; background-size=&quot;cover&quot;&gt;&lt;/image&gt;</div><div>  &lt;text class=&quot;userinfo-nickname&quot;&gt;{{userInfo.nickName}}&lt;/text&gt;</div><div>&lt;/view&gt; </div><div>--&gt;</div></pre></td></tr></tbody></table></div>\\\n<p>首先 <code>bindtap=&quot;bindViewTap&quot;</code> 给这个 View 绑定了一个点击事件，也就是我们前面 index.js 对应的这个方法，用户点击这个 View 就会跳转到 logs 页面上。 然后这个 View 里面包含了一个 Image 和 Text， Image 的 src 属性设置为 userInfo.avatarUrl， 代表当前用户的头像， Text 中使用 userInfo.nickName， 代表当前用户的昵称。</p>\\\n<p>这样， index 页面的整体逻辑就都完成了， 还有一个 index.wxss 样式文件，这个咱们就先略过。 </p>\\\n<p>再来看看第二个视图：</p>\\\n<div><table><tbody><tr><td><pre><div>&lt;!-- </div><div>&lt;view class=&quot;usermotto&quot;&gt;</div><div>    &lt;text class=&quot;user-motto&quot;&gt;{{motto}}&lt;/text&gt;</div><div>&lt;/view&gt; </div><div>--&gt;</div></pre></td></tr></tbody></table></div>\\\n<p>motto 其实就是我们在 index.js 中定义的一个属性：</p>\\\n<div><table><tbody><tr><td><pre><div>data: {</div><div>    motto: 'Hello World',</div><div>    userInfo: {}</div><div> }</div></pre></td></tr></tbody></table></div>\\\n<p> 它会在页面上显示一个 Hello World。</p>\\\n<p>现在，我们切换到调试界面， 就可以看到小程序的主页了， 和我们刚刚描述的 UI 完全一样吧：</p>\\\n<p><a href=\\\"http://swiftcafe.io/articleimg/wx-app/3.png\\\" target=\\\"_blank\\\"></a></p><div><a href=\\\"http://swiftcafe.io/articleimg/wx-app/3.png\\\" target=\\\"_blank\\\"><img src=\\\":/232cd2dcd1814a3f951ffc768ca04a38\\\" type=\\\"image/png\\\" data-filename=\\\"Image.png\\\" height=\\\"706\\\" width=\\\"477\\\"/></a></div><p></p>\\\n<p>这里的用户头像和昵称是动态从你的登录状态中取到的。</p>\\\n<p>然后我们在这里点击用户的头像，就会跳转到 logs 页面， 列出你每次登录小程序的时间点。 </p>\\\n<h2>上传小程序</h2><p>现在微信小程序的基本开发流程就给大家介绍完了，还有一个 logs 页面没介绍，但它和 index 的页面的基本思路都是一样的，咱们就不多赘述了。 开发完小程序后，我们需要把它部署到哪里呢？ 相信大家也有同样的问题。 答案也很简单，切换到<strong>项目</strong>选项卡，然后点击上传按钮即可：</p>\\\n<p><a href=\\\"http://swiftcafe.io/articleimg/wx-app/4.png\\\" target=\\\"_blank\\\"></a></p><div><a href=\\\"http://swiftcafe.io/articleimg/wx-app/4.png\\\" target=\\\"_blank\\\"><img src=\\\":/2ead20867c9444e8b00af97fe839d293\\\" type=\\\"image/png\\\" data-filename=\\\"Image.png\\\" height=\\\"371\\\" width=\\\"576\\\"/></a></div><p></p>\\\n<p>由于我的环境没有内测账号，所以在上传区域显示的是<code>项目未关联 AppID</code>， 如果有了测试账号，就会显示你的 AppID 了。 目前只有内测账号才能够上传小程序。这就是唯一的差别了。没有内测账号只是不能上传，但完全可以在本地开发和测试。</p>\\\n<p>小程序的这种上传方式可能会让大家觉得有些不同吧。 大家平常理解的 Web app 一般都需要自己搭建服务端，并且维护。 而小程序的这种托管方式，其实已经和我们开发一个原生 App 差不多了。 虽然前端上使用的是 js 这些看起来像是 web 的技术，但它核心思路跟传统的 web app 已经不太一样。 更像一种类似 React Native 的实现。</p>\\\n<h2>结尾</h2><p>这次和大家一起，从头到尾完整的体验了一个最简单的小程序的整体结构以及开发思路。个人觉得如果能找到合适的切入点，小程序这个平台还是能找到一些不错的机会的。但我的观点是，不要认为小程序的出现就会马上颠覆谁，也不需要听到网上有人说小程序难成大事就觉得它没机会。 找到自己擅长的，以及用户需要的，没准就会创造出一些不错的产品。 这次也是帮大家做一个基本的技术梳理，希望能够对大家有所帮助。</p>\\\n\\\n    \\t<p>如果你觉得这篇文章有帮助，还可以关注微信公众号 swift-cafe，会有更多我的原创内容分享给你~</p>\\\n    \\\n        \\\n        \\\n        本站文章均为原创内容，如需转载请注明出处，谢谢。<br/>\\\n\\t\\t<hr/>\\\n\\t\\t\\\n\\t\\t\\\n\\t  \\\n\\t\\t<br/><br/><br/>\\\n        \\\n\\t</div></div></div><br/></div></span>\\\n</div></body></html>\\u0000\"]],\"start1\":0,\"start2\":0,\"length1\":0,\"length2\":11246}]"
metadata_diff: {"new":{"id":"13ac1745af964e6b8d239fc434a7a919","parent_id":"061de1401cfc4cfaadf6e74e015970e9","latitude":"0.00000000","longitude":"0.00000000","altitude":"0.0000","author":"","source_url":"","is_todo":0,"todo_due":0,"todo_completed":0,"source":"joplin-desktop","source_application":"net.cozic.joplin-desktop","application_data":"","order":1764345822713,"markup_language":2,"is_shared":0,"share_id":"","conflict_original_id":"","master_key_id":"","user_data":"","deleted_time":0},"deleted":[]}
encryption_cipher_text: 
encryption_applied: 0
updated_time: 2025-11-28T16:20:17.937Z
created_time: 2025-11-28T16:20:17.937Z
type_: 13