Bug 1257322 - add more devtools docs r=pbrosset

This commit is contained in:
James Long 2016-03-21 10:36:28 -04:00
parent 98329bf0db
commit 5d7c236b16
17 changed files with 523 additions and 3 deletions

1
devtools/docs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_book

41
devtools/docs/README.md Normal file
View File

@ -0,0 +1,41 @@
# Firefox Developer Tools
Hello! This documentation is for developers who want to work on the
developer tools. If you are looking for general docs about how to use
the tools, checkout [this MDN
page](https://developer.mozilla.org/en-US/docs/Tools).
These docs explain how the developer tools work at high-level, as well
as providing links to reference documentation. This is a good starting
point if you are a new contributor, or want to learn how our protocol,
a specific tool, or something else works.
If you are looking to **start hacking** on the developer tools, all of
this information is documented on the
[Hacking](https://wiki.mozilla.org/DevTools/Hacking) wiki page.
A very quick version:
```
$ hg clone http://hg.mozilla.org/integration/fx-team
$ ./mach build
$ ./mach run -P development
```
You can also clone via git from
`https://github.com/mozilla/gecko-dev.git`. Note that the workflow for
submitting patches may be a little different if using git.
Please see the [Hacking](https://wiki.mozilla.org/DevTools/Hacking)
page for a lot more information!
All of our **coding standards** are documented on the [Coding
Standards](https://wiki.mozilla.org/DevTools/CodingStandards) wiki
page.
We use ESLint to enforce coding standards, and if you can run it
straight from the command like this:
```
./mach eslint path/to/directory
```

18
devtools/docs/SUMMARY.md Normal file
View File

@ -0,0 +1,18 @@
# Summary
* [Tool Architectures](tools.md)
* [Inspector](inspector-panel.md)
* [Memory](memory-panel.md)
* [Debugger](debugger-panel.md)
* [Frontend](frontend.md)
* [Panel SVGs](svgs.md)
* [React](react.md)
* [Guidelines](react-guidelines.md)
* [Tips](react-tips.md)
* [Redux](redux.md)
* [Guidelines](redux-guidelines.md)
* [Tips](redux-tips.md)
* [Backend](backend.md)
* [Protocol](protocol.md)
* [Debugger API](debugger-api.md)

2
devtools/docs/backend.md Normal file
View File

@ -0,0 +1,2 @@
These files provide information about general backend architecture.

View File

@ -0,0 +1,9 @@
The Debugger API is a low-level API that provides methods for
introspecting and affecting a target environment like a page. You can
find JavaScript sources, set breakpoints on them, and more.
This API is completely documented on the [Debgger
API](https://developer.mozilla.org/en-US/docs/Tools/Debugger-API) MDN
page

View File

@ -0,0 +1,2 @@
This is just a stub until we write debugger docs.

View File

@ -0,0 +1,2 @@
These files provide information about general frontend architecture.

View File

@ -0,0 +1,6 @@
The devtools is built around a client/server architecture because the
tools run outside of the process it debugs. This protocol is
documented extensively in the [Remote Debugging
Protocol](https://wiki.mozilla.org/Remote_Debugging_Protocol) wiki
page.

View File

@ -0,0 +1,73 @@
# Guidelines for Writing React
These are soft rules for writing react devtools code. Try to stick to
these for consistency, and if you disagree, file a bug to change these
docs and we can talk about it.
**Please also read** the [coding
standards](https://wiki.mozilla.org/DevTools/CodingStandards#React_.26_Redux)
for react and redux code. The guidelines here are more general
patterns not specific to code style.
### Why no JSX?
You probably already noticed we don't use JSX. The answer isn't
complicated: we don't build our JS code, and we write directly for our
JS engine, SpiderMonkey. It already supports much of ES6, but it does
not support JSX (which is not a standard).
This may change if we ever adopt a build step. Even so, the author is
not convinced that JSX helps enough to warrant all the syntax. It is
clearer sometimes, but it can be noisy switching between JSX and JS a
lot.
It's not as bad as you may think! If you are used to JSX it may be an
adjustment, but you won't miss it too much.
### One component per file
Try to only put one component in a file. This helps avoid large files
full of components, but it's also technically required for how we wrap
components with factories. See the next rule.
It also makes it easier to write tests because you might not export
some components, so tests can't access them.
You can include small helper components in the same file if you really
want to, but note that they won't be directly tested and you will have
to use `React.createElement` or immediately wrap them in factories to
use them.
### Export the component directly and create factory on import
Modules are the way components interact. Ideally every component lives
in a separate file and they require whatever they need. This allows
tests to access all components and use module boundaries to wrap
components.
For example, we don't use JSX, so we need to create factories for
components to use them as functions. A simple way to do this is on
import:
```js
const Thing1 = React.createFactory(require('./thing1'));
const Thing2 = React.createFactory(require('./thing2'));
```
It adds a little noise, but then you can do `Thing1({ ... })` instead
of `React.createElement(Thing1, { ... })`. Definitely worth it.
Additionally, make sure to export the component class directly:
```js
const Thing1 = React.createClass({ ... });
module.exports = Thing1;
```
Do not export `{ Thing1 }` or anything like that. This is required for
the factory wrapping as well as hot reloading.
### More to Come
This is just a start. We will add more to this document.

View File

@ -0,0 +1,19 @@
# React Tips
Learn various tips and tricks for working with our React code.
## Hot Reloading
If you followed [this
rule](react-guidelines.md#export-the-component-directly-and-create-factory-on-import)
about exporting components, and are using the BrowserLoader, you can
hot reload any React component. Just turn on the pref
`devtools.loader.hotreload`, re-open the devtools, and you should be
able to save any React component and instantly see changes.
This does not reset the state of your app, so you can quickly se
changes in the same context.
## Profiling
We need information here about how to use React.addons.Perf to profile the app.

157
devtools/docs/react.md Normal file
View File

@ -0,0 +1,157 @@
We use [React](http://facebook.github.io/react/) to write our user
interfaces. In here you can find an explanation of why we chose React
and a short primer on it. Additionally, we list best practices that
all devtools code should adhere to when writing React.
# Quick Intro
This is a very quick introduction on how to *use* React, but does not
explain in-depth the concepts behind it. If you want more in-depth
articles, I recommend the following links:
* http://facebook.github.io/react/docs/tutorial.html - the official tutorial
* https://github.com/petehunt/react-howto - how to learn React
* http://jlongster.com/Removing-User-Interface-Complexity,-or-Why-React-is-Awesome - long read but explains the concepts in depth
React embraces components as a way of thinking about UIs. Components
are the center of everything: they are composable like functions,
testable like JSON data, and provide lifecycle APIs for more complex
scenarios.
A component can represent anything from a single item in a list to a
complete virtualized grid that is made up of sub-components. They can
be used to abstract out "behaviors" instead of UI elements (think of a
`Selectable` component). React's API makes it easy to break up your UI
into whatever abstractions you need.
The core idea of a component is simple: it's something that takes
properties and returns a DOM-like structure.
```js
function Item({ name, iconURL }) {
return div({ className: "item" },
img({ className: "icon", href: iconURL }),
name);
}
```
The `div` and `span` functions refer to `React.DOM.div` and
`React.DOM.span`. React provides constructors for all DOM elements on
`React.DOM`. These conform to the standard API for creating elements:
the first argument takes properties, and the rest are children.
You can see component composition kick in when using `Item`:
```js
const Item = React.createFactory(require('./Item'));
function List({ items }) {
return div({ className: "list" },
items.map(item => Item({ name: item.name, icon: item.iconURL)));
}
```
You can use custom components exactly the same way you use native
ones! The only difference is we wrapped it in a factory when importing
instead of using the React.DOM functions. Factories are just a way of
turning a component into a convenient function. Without factories, you
need to do do `React.createElement(Item, { ... })`, which is exactly
the same as `Item({ ... })` if using a factory.
## Rendering and Updating Components
Now that we have some components, how do we render them? You use
`React.render` for that:
```js
let items = [{ name: "Dubois", iconURL: "dubois.png" },
{ name: "Ivy", iconURL: "ivy.png" }];
React.render(List({ items: items }),
document.querySelector("#mount"));
```
This renders a `List` component, given `items`, to a DOM node with an
id of `mount`. Typically you have a top-level `App` component that is
the root of everything, and you would render it like so.
What about updating? First, let's talk about data. The above
components take data from above and render out DOM structure. If any
user events were involved, the components would call callbacks passed
as props, so events walk back up the hierarchy. The conceptual model
is data goes down, and events come up.
You usually want to change data in response to events, and rerender
the UI with the new data. What does that look like? There are two
places where React will rerender components:
1\. Any additional `React.render` calls. Once a component is mounted,
you can call `React.render` again to the same place and React will see
that it's already mounted and perform an update instead of a full
render. For example, this code adds an item in response to an event
and updates the UI, and will perform optimal incremental updates:
```js
function addItem(item) {
render([...items, item]);
}
function render(items) {
React.render(List({ items: items,
onAddItem: addItem }),
document.querySelector("#mount"));
}
render(items);
```
2\. Changing component local state. This is much more common. React
allows components to have local state, and whenever the state is
changed with the `setState` API it will rerender that specific
component. If you use component local state, you need to create a
component with `createClass`:
```js
const App = React.createClass({
getInitialState: function() {
return { items: [] };
},
handleAddItem: function(item) {
const items = [...this.props.items, item];
this.setState({ items: items });
},
render: function() {
return List({ items: this.state.items,
onAddItem: this.handleAddItem });
}
});
```
If you are using something like Redux to manage state this is handled
automatically for you with the library you use to bind Redux with
React. See more in [Redux](redux.html).
## DOM Diffing
What does it mean when React "updates" a component, and how does it
know which DOM to change? React achieves this with a technique called
DOM diffing. This alleviates the need for the programmer to worry
about how updates are actually applied to the DOM, and components can
render DOM structure declaratively in response to data. In the above
examples, when adding an item, React knows to only add a new DOM node
instead of recreating the whole list each time.
DOM diffing is possible because our components return what's called
"virtual DOM": a lightweight JSON structure that React can use to diff
against previous versions, and generate minimal changes to the real DOM.
This also makes it really east to test components with a real DOM:
just make sure the virtual DOM has what it should.
## Next
Read the [React Guidelines](react-guidelines.md) next to learn how to
write React code specifically for the devtools.

View File

@ -0,0 +1,10 @@
Need to answer the following questions:
* How do I do I load asynchronous data?
* How do I do optimistic updates or respond to errors from async work?
* Do I use Immutable.js for my state?
* What file structure should I use?
* How do I test redux code?
And more.

View File

@ -0,0 +1,5 @@
Need to document:
* How to attach various redux loggers
* How to hot reload redux code

160
devtools/docs/redux.md Normal file
View File

@ -0,0 +1,160 @@
We use [Redux](https://github.com/reactjs/redux) to manage application
state. The [docs](http://redux.js.org/) do a good job explaining the
concepts, so go read them.
# Quick Intro
Just like the [React introduction](react.html), this is a quick
introduction to redux, focusing on how it fits into React and why we
chose it.
One of the core problems that React does not address is managing
state. In the React intro, we talked about data flowing down and
events flowing up. Conceptually this is nice, but you quickly run into
awkward situations in large apps.
Let's look at an example. Say you have a page with a tabbed interface.
Here, `Tab1` is managing a list of items, so naturally it uses local
state. `Tab2` renders different stuff.
```js
const Tab1 = React.createClass({
getInitialState: function() {
return { items: [] };
},
handleAddItem: function(item) {
this.setState({ items: [...this.state.items, item]});
},
render: function() {
/* ... Renders the items and button to add new item ... */
}
});
const Tab2 = React.createClass({
render: function() {
/* ... Renders other data ... */
}
});
// Assume `Tab1` and `Tab2` are wrapped with a factory when importing
const Tabs = React.createClass({
render: function() {
return div(
{ className: 'tabs' },
// ... Render the tab buttons ...
Tab1(),
Tab2()
);
}
});
```
What happens when `Tab2` needs the list of items though? This scenario
comes up all time: components that aren't directly related need access
to the same state. A small change would be to move the `items` state
up to the `Tabs` component, and pass it down to both `Tab1` and `Tab2`.
But now `Tabs` has to implement the `handleAddItem` method to add an
item because it's managing that state. This quickly gets ugly as the
end result is the root component ends up with a ton of state and
methods to manage it: a [god
component](https://en.wikipedia.org/wiki/God_object) is born.
Additionally, how do we know what data each tab needs? We end up
passing *all* the state down because we don't know. This is not a
modular solution: one object managing the state and every component
receiving the entire state is like using tons of global variables.
## Evolution of Flux
Facebook addressed this with the
[flux](https://facebook.github.io/flux/) architecture, which takes the
state out of the components and into a "store". Redux is the latest
evolution of this idea and solves a lot of problems previous flux
libraries had (read it's documentation for more info).
Because the state exists outside the component tree, any component can
read from it. Additionally, **state is updated with
[actions](http://redux.js.org/docs/basics/Actions.html)** that any
component can fire. We have [guidelines](redux-guidelines) for where
to read/write state, but it completely solves the problem described
above. Both `Tab1` and `Tab2` would be listening for changes in the
`item` state, and `Tab1` would fire actions to change it.
With redux, **state is managed modularly with
[reducers](http://redux.js.org/docs/basics/Reducers.html)** but tied
together into a single object. This means a single JS object
represents most* of your state. It may sound crazy at first, but think
of it as an object with references to many pieces of state; that's all
it is.
This makes it very easy to test, debug, and generally think about. You
can log your entire state to the console and inspect it. You can even
dump in old states and "replay" to see how the UI changed over time.
I said "most*" because it's perfectly fine to use both component local
state and redux. Be aware that any debugging tools will not see local
state at all though. It should only be used for transient state; we'll
talk more about that in the guidelines.
## Immutability
Another important concept is immutability. In large apps, mutating
state makes it very hard to track what changed when. It's very easy to
run into situations where something changes out from under you, and
the UI is rendered with invalid data.
Redux enforces the state to be updated immutably. That means you
always return new state. It doesn't mean you do a deep copy of the
state each time: when you need to change some part of the tree you
only need to create new objects to replace the ones your changing (and
walk up to the root to create a new root). Unchanged subtrees will
reference the same objects.
This removes a whole class of errors, almost like Rust removing a
whole class of memory errors by enforcing ownership.
## Order of Execution
One of best things about React is that **rendering is synchronous**. That
means when you render a component, given some data, it will fully
render in the same tick. If you want the UI to change over time, you
have to change the *data* and rerender, instead of arbitrary UI
mutations.
The reason this is desired is because if you build the UI around
promises or event emitters, updating the UI becomes very brittle
because anything can happen at any time. The state might be updated in
the middle of rendering it, maybe because you resolved a few promises
which made your rendering code run a few ticks later.
Redux embraces the synchronous execution semantics as well. What this
means is that everything happens in a very controlled way. When
updating state through an action, all reducers are run and a new state
is synchronously generated. At that point, the new state is handed off
to React and synchronously rendered.
Updating and rendering happen in two phases, so the UI will *always*
represent consistent state. The state can never be in the middle of
updating when rendering.
What about asynchronous work? That's where
[middleware](http://redux.js.org/docs/advanced/Middleware.html) come
in. At this point you should probably go study our code, but
middleware allows you to dispatch special actions that indicate
asynchronous work. The middleware will catch these actions and do
something async, dispatching "raw" actions along the way (it's common
to emit a START, DONE, and ERROR action).
**Ultimately there are 3 "phases" or level of abstraction**: the async
layer talks to the network and may dispatch actions, actions are
synchronously pumped through reducers to generate state, and state is
rendered with react.
## Next
Read the [Redux Guidelines](redux-guidelines.md) next to learn how to
write React code specifically for the devtools.

View File

@ -0,0 +1,13 @@
.book .book-summary ul.summary li {
cursor: pointer;
}
.book .book-body .page-wrapper .page-inner section.normal p,
.book .book-body .page-wrapper .page-inner section.normal pre {
margin: .85em 0;
}
.book .book-body .page-wrapper .page-inner section.normal pre {
line-height: 1.25em;
}

View File

@ -24,10 +24,10 @@ You can download a sample Illustrator file [here](https://www.dropbox.com/home/M
When you're designing your icons in a graphics editor like Adobe Illustrator, there are a lot of things you can do that will bring down the size of the file and make your SVGs easier for the developers to work with. Here are some of them:
- **Expand paths**: Instead of having multiple shapes overlapping each other, expand shapes using the pathfinder.
![Use pathfinder to expand shapes](./pathfinder.gif)
![Use pathfinder to expand shapes](./svgs/pathfinder.gif)
- Simplify paths (```Object``` > ```Path``` > ```Simplify```)
- Expand objects so that strokes become objects. This has the added benefit of keeping the stroke size intact as the SVG is resized.
![Expand strokes to make them objects](./expand-strokes.gif)
![Expand strokes to make them objects](./svgs/expand-strokes.gif)
## Sketch
Sketch vector work is a little different but the fundamentals (keeping your SVG small, expanding all paths) is the same. Here's what we've found helps to build clean icons:
@ -35,7 +35,7 @@ Sketch vector work is a little different but the fundamentals (keeping your SVG
- **Build your icon at 16x16 with the Pixel Grid turned on.** You can turn the pixel grid on at ```View > Canvas > Show Pixels```
- **Make sure that all x/y coordinates are full pixels for lines/rectangles.** Sub-pixels = not on pixel grid.
![Position in the upper right hand corner of Sketch](./sketch-position.png)
![Position in the upper right hand corner of Sketch](./svgs/sketch-position.png)
- **Expand all your paths so strokes expand properly as the SVG gets resized.** You can do this at ```Layer > Paths > Vectorize Stroke```.

2
devtools/docs/tools.md Normal file
View File

@ -0,0 +1,2 @@
These files provide a high-level overview of each tool and how they work.